diff options
Diffstat (limited to 'src/ssx/ssx')
-rwxr-xr-x | src/ssx/ssx/Makefile | 25 | ||||
-rwxr-xr-x | src/ssx/ssx/ssx.h | 126 | ||||
-rwxr-xr-x | src/ssx/ssx/ssx_api.h | 888 | ||||
-rwxr-xr-x | src/ssx/ssx/ssx_core.c | 81 | ||||
-rwxr-xr-x | src/ssx/ssx/ssx_init.c | 159 | ||||
-rwxr-xr-x | src/ssx/ssx/ssx_kernel.h | 281 | ||||
-rwxr-xr-x | src/ssx/ssx/ssx_macros.h | 119 | ||||
-rwxr-xr-x | src/ssx/ssx/ssx_semaphore_core.c | 331 | ||||
-rwxr-xr-x | src/ssx/ssx/ssx_semaphore_init.c | 84 | ||||
-rwxr-xr-x | src/ssx/ssx/ssx_stack_init.c | 87 | ||||
-rwxr-xr-x | src/ssx/ssx/ssx_thread_core.c | 946 | ||||
-rwxr-xr-x | src/ssx/ssx/ssx_thread_init.c | 140 | ||||
-rwxr-xr-x | src/ssx/ssx/ssx_timer_core.c | 447 | ||||
-rwxr-xr-x | src/ssx/ssx/ssx_timer_init.c | 124 | ||||
-rwxr-xr-x | src/ssx/ssx/ssxssxfiles.mk | 35 |
15 files changed, 3873 insertions, 0 deletions
diff --git a/src/ssx/ssx/Makefile b/src/ssx/ssx/Makefile new file mode 100755 index 0000000..ce1116f --- /dev/null +++ b/src/ssx/ssx/Makefile @@ -0,0 +1,25 @@ +# $Id: Makefile,v 1.2 2013/12/12 16:12:38 bcbrock Exp $ + +# This Makefile is designed to be invoked with the -I argument set to +# the location of the "ssx.mk" for the build + +include ../pgp/ssx.mk +include ssxssxfiles.mk + +ifeq "$(SSX_TIMER_SUPPORT)" "1" +SSX_OBJECTS += ${SSX-TIMER-C-SOURCES:.c=.o} +endif + +ifeq "$(SSX_THREAD_SUPPORT)" "1" +SSX_OBJECTS += ${SSX-THREAD-C-SOURCES:.c=.o} +endif + +all: $(SSX_OBJECTS) + +.PHONY : clean +clean: + rm -f *.o *.d *.d.* + +ifneq ($(MAKECMDGOALS),clean) +include $(SSX_OBJECTS:.o=.d) +endif diff --git a/src/ssx/ssx/ssx.h b/src/ssx/ssx/ssx.h new file mode 100755 index 0000000..5470968 --- /dev/null +++ b/src/ssx/ssx/ssx.h @@ -0,0 +1,126 @@ +#ifndef __SSX_H__ +#define __SSX_H__ + +// $Id: ssx.h,v 1.1.1.1 2013/12/11 21:03:27 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssx.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx.h +/// \brief The combined header of the SSX kernel. +/// +/// This header will be included in any C or assembler source file that +/// requires any of the SSX API. All headers defined by SSX and co-compiled +/// code should be protected such that they can be included without error into +/// assembly. + +#ifndef __ASSEMBLER__ +#include <stdint.h> +#include <stddef.h> +#endif /* __ASSEMBLER__ */ + +#define __SSX__ + +/// The application environment specifies whether or not it will provide an +/// application configuration file, which must be named "ssx_app_cfg.h". + +#ifndef USE_SSX_APP_CFG_H +#define USE_SSX_APP_CFG_H 0 +#endif + +#if USE_SSX_APP_CFG_H +#include "ssx_app_cfg.h" +#endif + +#include "ssx_macros.h" +#include "ssx_api.h" +#include "ssx_port.h" +#include "ssx_kernel.h" +#include "ssx_io.h" + +#ifndef __ASSEMBLER__ + +#define MIN(X, Y) \ + ({ \ + typeof (X) __x = (X); \ + typeof (Y) __y = (Y); \ + (__x < __y) ? __x : __y; }) + +#define MAX(X, Y) \ + ({ \ + typeof (X) __x = (X); \ + typeof (Y) __y = (Y); \ + (__x > __y) ? __x : __y; \ + }) + +/// \todo These don't require 32/64 bit versions, can always promote 32->64. + +#define FLOOR_LOG2_32(x) (32 - 1 - cntlz32(x)) +#define FLOOR_LOG2_64(x) (64 - 1 - cntlz64(x)) + +#define CEILING_LOG2(x) \ + ({ \ + uint64_t __x = (uint64_t)(x); \ + int __y; \ + __y = FLOOR_LOG2_64(__x); \ + if ((__x & (__x - 1)) != 0) { \ + __y++; \ + } \ + __y;}) + + +#define POW2_32(x) ((uint32_t)1 << (x)) +#define POW2_64(x) ((uint64_t)1 << (x)) + +/// Cast a pointer to another type +/// +/// This macro is necessary when casting a pointer to a longer integer type. +/// The pointer is first cast to the equivalent integer type 'unsigned long', +/// then cast to the final type. You can also use this to cast integers longer +/// than pointers back to pointers. + +#define CAST_POINTER(t, p) ((t)((unsigned long)(p))) + + +/// Create an alignment attribute. +#define ALIGNED_ATTRIBUTE(alignment) __attribute__ ((aligned (alignment))) + +/// Create a specific-section attribute +/// +/// Note that the section \a s here must be a string. Also note that data +/// specified to reside in specific sections must always be +/// initialized. Otherwise it confuses the linker which wants to put +/// uninitialized data into .bss sections. +/// +/// \code +/// +/// int foo SECTION_ATTRIBUTE(".noncacheable") = 0; +/// int bar[10] SECTION_ATTRIBUTE(".noncacheable") = {0}; +/// +/// \endcode +#define SECTION_ATTRIBUTE(s) __attribute__ ((section (s))) + +/// Create a 'used' attribute +/// +/// This is required for example to avoid "function unused" warnings when a +/// function is declared static but only referenced by inline assembler: +/// +/// \code +/// +/// static USED_ATTRIBUTE void +/// _checkstop(void* arg, SsxIrqId irq, int priority) +/// { +/// SSX_PANIC(VALIDATION_CHECKSTOP); +/// } +/// +/// SSX_IRQ_FAST2FULL(_validationCheckstopHandler, _checkstop); +/// +/// \endcode +#define USED_ATTRIBUTE __attribute__ ((used)) + +#endif /* __ASSEMBLER__ */ + +#endif /* __SSX_H__ */ diff --git a/src/ssx/ssx/ssx_api.h b/src/ssx/ssx/ssx_api.h new file mode 100755 index 0000000..c9657e4 --- /dev/null +++ b/src/ssx/ssx/ssx_api.h @@ -0,0 +1,888 @@ +#ifndef __SSX_API_H__ +#define __SSX_API_H__ + +// $Id: ssx_api.h,v 1.2 2014/02/03 01:30:44 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssx_api.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_api.h +/// \brief Macros and declarations for the SSX API. + +// Basic constants + +/// Although the number of threads is defined as a manifest constant, +/// numerous parts of the SSX code assume this definition. The number of +/// supported threads _can not_ be changed simply by changing this constant. + +#define SSX_THREADS 32 + +#define SSX_IDLE_THREAD_PRIORITY SSX_THREADS + +// Interrupt API + +#define SSX_NONCRITICAL 0 +#define SSX_CRITICAL 1 +#define SSX_SUPERCRITICAL 2 + +#define SSX_IRQ_POLARITY_ACTIVE_LOW 0 +#define SSX_IRQ_POLARITY_ACTIVE_HIGH 1 + +#define SSX_IRQ_TRIGGER_LEVEL_SENSITIVE 0 +#define SSX_IRQ_TRIGGER_EDGE_SENSITIVE 1 + +// API return codes + +#define SSX_OK 0 +#define SSX_ILLEGAL_CONTEXT_CRITICAL_INTERRUPT 0x00779001 +#define SSX_ILLEGAL_CONTEXT_THREAD_CONTEXT 0x00779002 +#define SSX_ILLEGAL_CONTEXT_INTERRUPT_CONTEXT 0x00779003 +#define SSX_ILLEGAL_CONTEXT_THREAD 0x00779004 +#define SSX_ILLEGAL_CONTEXT_TIMER 0x00779005 +#define SSX_ILLEGAL_CONTEXT_PPC405_CACHE 0x00779006 +#define SSX_INVALID_THREAD_AT_RESUME1 0x00779007 +#define SSX_INVALID_THREAD_AT_RESUME2 0x00779008 +#define SSX_INVALID_THREAD_AT_SUSPEND1 0x00779009 +#define SSX_INVALID_THREAD_AT_SUSPEND2 0x0077900a +#define SSX_INVALID_THREAD_AT_DELETE 0x0077900b +#define SSX_INVALID_THREAD_AT_INFO 0x0077900c +#define SSX_INVALID_THREAD_AT_CHANGE 0x0077900d +#define SSX_INVALID_THREAD_AT_SWAP1 0x0077900e +#define SSX_INVALID_THREAD_AT_SWAP2 0x0077900f +#define SSX_INVALID_THREAD_AT_CREATE 0x00779010 +#define SSX_INVALID_SEMAPHORE_AT_POST 0x00779011 +#define SSX_INVALID_SEMAPHORE_AT_PEND 0x00779012 +#define SSX_INVALID_SEMAPHORE_AT_RELEASE 0x00779013 +#define SSX_INVALID_SEMAPHORE_AT_INFO 0x00779014 +#define SSX_INVALID_SEMAPHORE_AT_CREATE 0x00779015 +#define SSX_INVALID_TIMER_AT_SCHEDULE 0x00779016 +#define SSX_INVALID_TIMER_AT_CANCEL 0x00779017 +#define SSX_INVALID_TIMER_AT_INFO 0x00779018 +#define SSX_INVALID_TIMER_AT_CREATE 0x00779019 +#define SSX_INVALID_ARGUMENT_IRQ_SETUP 0x0077901a +#define SSX_INVALID_ARGUMENT_IRQ_HANDLER 0x0077901b +#define SSX_INVALID_ARGUMENT_INTERRUPT 0x00779024 +#define SSX_INVALID_ARGUMENT_CONTEXT_SET 0x00779025 +#define SSX_INVALID_ARGUMENT_CONTEXT_GET 0x00779026 +#define SSX_INVALID_ARGUMENT_PPC405_FIT 0x00779027 +#define SSX_INVALID_ARGUMENT_PPC405_WATCHDOG 0x00779028 +#define SSX_INVALID_ARGUMENT_INIT 0x00779029 +#define SSX_INVALID_ARGUMENT_SEMAPHORE 0x0077902a +#define SSX_INVALID_ARGUMENT_THREAD_CHANGE 0x0077902b +#define SSX_INVALID_ARGUMENT_THREAD_PRIORITY 0x0077902c +#define SSX_INVALID_ARGUMENT_THREAD1 0x0077902d +#define SSX_INVALID_ARGUMENT_THREAD2 0x0077902e +#define SSX_INVALID_ARGUMENT_THREAD3 0x0077902f +#define SSX_STACK_OVERFLOW 0x00779030 +#define SSX_TIMER_ACTIVE 0x00779031 +#define SSX_TIMER_NOT_ACTIVE 0x00779032 +#define SSX_PRIORITY_IN_USE_AT_RESUME 0x00779033 +#define SSX_PRIORITY_IN_USE_AT_CHANGE 0x00779034 +#define SSX_PRIORITY_IN_USE_AT_SWAP 0x00779035 +#define SSX_SEMAPHORE_OVERFLOW 0x00779036 +#define SSX_SEMAPHORE_PEND_NO_WAIT 0x00779037 +#define SSX_SEMAPHORE_PEND_TIMED_OUT 0x00779038 +#define SSX_SEMAPHORE_PEND_WOULD_BLOCK 0x00779039 +#define SSX_INVALID_DEQUE_SENTINEL 0x0077903a +#define SSX_INVALID_DEQUE_ELEMENT 0x0077903b +#define SSX_INVALID_OBJECT 0x0077903c + +// Kernel panics + +#define SSX_NO_TIMER_SUPPORT 0x0077903d +#define SSX_START_THREADS_RETURNED 0x0077903e +#define SSX_UNIMPLEMENTED 0x0077903f +#define SSX_SCHEDULING_INVARIANT 0x00779040 +#define SSX_TIMER_HANDLER_INVARIANT 0x00779041 +#define SSX_THREAD_TIMEOUT_STATE 0x00779045 + + +/// \defgroup ssx_thread_states SSX Thread States +/// +/// Threads are created in the state SSX_THREAD_STATE_SUSPENDED_RUNNABLE. +/// When the thread is mapped it transitions to state SSX_THREAD_STATE_MAPPED. +/// A mapped thread is runnable if it appears in the run queue; there is no +/// other flag or status to indicate a runnable thread. If a blocked thread +/// is suspended it goes into state SSX_THREAD_STATE_SUSPENDED_BLOCKED. For +/// all threads the reason for blockage is detailed in the \a flags field of +/// the thread; See \ref ssx_thread_flags. SSX_THREAD_STATE_DELETED and +/// SSX_THREAD_STATE_COMPLETED are effectively equivalent but named +/// individually for reporting purposes. +/// +/// \note This separation of the thread \a state and \a flags allows the use +/// of an SSX semaphore as a thread barrier, as it supports a non-iterative +/// implementation of ssx_semaphore_release_all() in which all threads blocked +/// on the semaphore are simultaneously inserted into the run queue with an +/// atomic operation, followed by each individual thread readjusting its flags +/// appropriately once the thread runs again. +/// +/// @{ + +#define SSX_THREAD_STATE_SUSPENDED_RUNNABLE 1 +#define SSX_THREAD_STATE_MAPPED 2 +#define SSX_THREAD_STATE_SUSPENDED_BLOCKED 3 +#define SSX_THREAD_STATE_COMPLETED 4 +#define SSX_THREAD_STATE_DELETED 5 + +/// @} + + +/// \defgroup ssx_thread_flags SSX Thread Flags +/// +/// The \a flag field of the thread extends the information contained in the +/// \a state field; See \ref ssx_thread_states. Blocked threads will show +/// SSX_THREAD_FLAG_SEMAPHORE_PEND, SSX_THREAD_FLAG_TIMER_PEND or both (if +/// blocked on a semaphore with timeout). The flag SSX_THREAD_FLAG_TIMED_OUT +/// indicates that a thread timer timed out before the thread became +/// runnable. Currently only the semaphore-pend-with-timeout code uses this +/// flag. +/// +/// Note that a thread can be mapped and runnable (in the run queue) even +/// though SSX_THREAD_FLAG_SEMAPHORE_PEND and/or SSX_THREAD_FLAG_TIMER_PEND +/// are set. These flags are always cleared by the thread itself, not the code +/// that unblocks the thread. This allows the implementation of the +/// ssx_semaphore_release_all() as explained in \ref ssx_thread_states. +/// +/// @{ + +#define SSX_THREAD_FLAG_SEMAPHORE_PEND 0x1 +#define SSX_THREAD_FLAG_TIMER_PEND 0x2 +#define SSX_THREAD_FLAG_TIMED_OUT 0x4 + +/// @} + + +// Critical Sections + +/// Enter a critical section of a given priority, saving the current machine +/// context. + +#define ssx_critical_section_enter(priority, pctx) \ + ssx_interrupt_disable(priority, pctx) + +/// Exit a critical section by restoring the previous machine context. + +#define ssx_critical_section_exit(pctx) \ + ssx_machine_context_set(pctx) + + +/// Execute a statement atomically, in a particular interrupt priority +/// context. + +#define SSX_ATOMIC(priority, stmt) \ + do { \ + SsxMachineContext __ctx; \ + ssx_critical_section_enter((priority), &__ctx); \ + stmt; \ + ssx_critical_section_exit(&__ctx); \ + } while (0) + + +// Application-overrideable definitions + +/// Control whether or not the API functions check for errors. +/// +/// This definition can be overriden by the application. + +#ifndef SSX_ERROR_CHECK_API +#define SSX_ERROR_CHECK_API 1 +#endif + +/// Control whether API errors cause kernel panics or return negative error +/// codes. +/// +/// This selection is only valid if \c SSX_ERROR_CHECK_API is defined +/// non-0. This definition can be overriden by the application. + +#ifndef SSX_ERROR_PANIC +#define SSX_ERROR_PANIC 1 +#endif + +/// Control whether or not the SSX kernel checks key invariants. +/// +/// Violations of kernel invariants always cause kernel panics. This +/// definition can be overriden by the application. + +#ifndef SSX_ERROR_CHECK_KERNEL +#define SSX_ERROR_CHECK_KERNEL 1 +#endif + +/// Define the time interval type, which must be an unsigned type of a size +/// less then or equal to the size of \c SsxTimebase. This definition can be +/// overridden by the application. + +#ifndef SSX_TIME_INTERVAL_TYPE +#define SSX_TIME_INTERVAL_TYPE uint32_t +#endif + +/// Provide support for the SsxTimer APIs in addition to the default +/// initerrupt APIs. This definition can be overridden by the application. + +#ifndef SSX_TIMER_SUPPORT +#define SSX_TIMER_SUPPORT 1 +#endif + +/// Provide support for the all SSX APIs. Thread support requires/implies +/// support for time services and semaphores. This definition can be +/// overridden by the application. + +#ifndef SSX_THREAD_SUPPORT +#define SSX_THREAD_SUPPORT 1 +#endif + +/// Control the level of stack checking. +/// +/// This definition can be overriden by the application. +/// +/// 0 : No stack prepatterning or checking is made for thread and kernel +/// stacks. +/// +/// 1 : Kernel interrupt stacks are prepatterned during +/// \c ssx_initialize(). Thread stacks are prepatterned during +/// \c ssx_thread_create(). +/// +/// 2 : (\b Default - Currently Unimplemented) In addition to prepatterning, +/// stack utilization is computed at the exit of context switches and +/// noncritical interrupt processing. The maximum utilization is stored in +/// the thread data structure. The kernel will panic if stack overflow is +/// detected. Stack utilization is not computed for the idle thread. + +#ifndef SSX_STACK_CHECK +#define SSX_STACK_CHECK 1 +#endif + +/// A hook for main() +/// +/// This hook macro is expanded in the body of __ssx_main() prior to the call +/// of the application main(). The application can redefine this hook macro +/// in (or in headers referred to in) the application header +/// ssx_app_cfg.h. The SSX_MAIN_HOOK will run on the stack of main(). + +#ifndef SSX_MAIN_HOOK +#define SSX_MAIN_HOOK do {} while (0) +#endif + +/// A hook for ssx_start_threads() +/// +/// This hook macro is expanded in the call-tree of ssx_start_threads() before +/// threads are actually started. The application can redefine this hook +/// macro in (or in headers referred to in) the application header +/// ssx_app_cfg.h. +/// +/// The SSX_START_THREADS_HOOK runs as a pseudo-interrupt handler on the +/// noncritical interrupt stack, with noncritical interrupts disabled. + +#ifndef SSX_START_THREADS_HOOK +#define SSX_START_THREADS_HOOK do {} while (0) +#endif + +/// The maximum value of the \c SsxTimebase type. + +#define SSX_TIMEBASE_MAX ((SsxTimebase)-1) + +/// A special value that specifies that the timebase will not be reset during +/// ssx_init(). + +#define SSX_TIMEBASE_CONTINUES SSX_TIMEBASE_MAX + +/// By convention, a timeout value indicating 'no waiting' in a call of \c +/// ssx_semaphore_pend(). + +#define SSX_NO_WAIT 0 + +/// By convention, a timeout value indicating 'wait forever' in a call of \c +/// ssx_semaphore_pend(). + +#define SSX_WAIT_FOREVER ((SsxInterval)-1) + +/// The SSX timebase frequency in Hz +/// +/// Earlier version of SSX defined the timbase frequency as a preprocessor +/// macro. Now, the timebase frequency is specified as a parameter of the +/// ssx_initialize() API. The macro remains defined for backwards +/// compatibility, however all kernel uses of the timebase frequency are now +/// optimized around the timebase parameter. + +#define SSX_TIMEBASE_FREQUENCY_HZ __ssx_timebase_frequency_hz + +/// Convert a time in integral seconds to a time interval - overflows are +/// ignored. The application can redefine this macro. + +#ifndef SSX_SECONDS +#define SSX_SECONDS(s) ((SsxInterval)(__ssx_timebase_frequency_hz * (SsxInterval)(s))) +#endif + +/// Convert a time in integral milliseconds to a time interval - overflows are +/// ignored, and a frequency evenly (or closely) divisible by 1000 is +/// assumed. The application can redefine this macro. + +#ifndef SSX_MILLISECONDS +#define SSX_MILLISECONDS(m) ((SsxInterval)(__ssx_timebase_frequency_khz * (SsxInterval)(m))) +#endif + +/// Convert a time in integral microseconds to a time interval - overflows are +/// ignored, and a frequncy evenly (or closely) divisible by 1,000,000 is +/// assumed. The application can redefine this macro. + +#ifndef SSX_MICROSECONDS +#define SSX_MICROSECONDS(u) ((SsxInterval)(__ssx_timebase_frequency_mhz * (SsxInterval)(u))) +#endif + +/// Convert a time in integral nanoseconds to a time interval - overflows are +/// ignored, and a frequeyncy evenly (or closely) divisible by 1,000,000 is +/// assumed. The application can redefine this macro. + +#ifndef SSX_NANOSECONDS +#define SSX_NANOSECONDS(n) \ + ((SsxInterval)((__ssx_timebase_frequency_mhz * (SsxInterval)(n)) / 1000)) +#endif + + +// Application and kernel tracing. Tracing can only be enabled if the port +// defines the trace macros in that case. + +/// Enable SSX application tracing +#ifndef SSX_TRACE_ENABLE +#define SSX_TRACE_ENABLE 0 +#endif + +/// Enable SSX kernel tracing +#ifndef SSX_KERNEL_TRACE_ENABLE +#define SSX_KERNEL_TRACE_ENABLE 0 +#endif + +#if !SSX_TRACE_ENABLE +#define SSX_TRACE(event) +#endif + +#if !SSX_KERNEL_TRACE_ENABLE + +#define SSX_TRACE_THREAD_SLEEP(priority) +#define SSX_TRACE_THREAD_WAKEUP(priority) +#define SSX_TRACE_THREAD_SEMAPHORE_PEND(priority) +#define SSX_TRACE_THREAD_SEMAPHORE_POST(priority) +#define SSX_TRACE_THREAD_SEMAPHORE_TIMEOUT(priority) +#define SSX_TRACE_THREAD_SUSPENDED(priority) +#define SSX_TRACE_THREAD_DELETED(priority) +#define SSX_TRACE_THREAD_COMPLETED(priority) +#define SSX_TRACE_THREAD_MAPPED_RUNNABLE(priority) +#define SSX_TRACE_THREAD_MAPPED_SEMAPHORE_PEND(priority) +#define SSX_TRACE_THREAD_MAPPED_SLEEPING(priority) + + +#endif /* SSX_KERNEL_TRACE_ENABLE */ + + +#ifndef __ASSEMBLER__ + +#include <stddef.h> +#include <stdint.h> + +/// The timebase frequency in Hz; A parameter to ssx_initialize() +extern uint32_t __ssx_timebase_frequency_hz; + +/// The timebase frequency in KHz +extern uint32_t __ssx_timebase_frequency_khz; + +/// The timebase frequency in Mhz +extern uint32_t __ssx_timebase_frequency_mhz; + + +typedef unsigned long int SsxAddress; + +typedef uint8_t SsxThreadState; + +typedef uint8_t SsxThreadPriority; + +typedef uint8_t SsxThreadFlags; + +typedef uint32_t SsxSemaphoreCount; + +typedef uint64_t SsxTimebase; + +typedef SSX_TIME_INTERVAL_TYPE SsxInterval; + +#include "ssx_port_types.h" + +typedef struct { + + /// A priority queue of threads pending on the semaphore. + SsxThreadQueue pending_threads; + + /// The current semaphore count. + SsxSemaphoreCount count; + + /// The maximum allowable count - for error checking. + SsxSemaphoreCount max_count; + +} SsxSemaphore; + + +/// Compile-time initialize an SsxSemaphore structure +/// +/// This low-level macro creates a structure initializatin of an SsxSemaphore +/// structure. This can be used for example to create compile-time initialized +/// arrays of semaphores. +#define SSX_SEMAPHORE_INITIALIZATION(_initial_count, _max_count) \ + {.pending_threads = 0, \ + .count = (_initial_count), \ + .max_count = (_max_count)} + + +/// Declare and initialize a semaphore +#define SSX_SEMAPHORE(sem, initial_count, max_count) \ + SsxSemaphore sem = SSX_SEMAPHORE_INITIALIZATION(initial_count, max_count) + + +/// A generic doubly-linked list object +/// +/// This object functions both as a sentinel mode for a deque as well as a +/// pointer container for elements in deques. The SSX API assumes that +/// queueable structures will be defined with an SsxDeque structure as the +/// initial 'data member' of the structure. This allows a pointer to a queue +/// element to be cast to a pointer to an SsxDeque and vice-versa. + +typedef struct SsxDeque { + + /// Pointer to the head or the next element in a deque. + /// + /// When an SsxDeque is used as the sentinel node for a queue, \a next + /// points to the head of the queue, and the condition (next == \<self\>) + /// indicates an empty SsxDeque. By convention the condition (\a next == + /// 0) is used to indicate that a queue element is not enqueued. + struct SsxDeque* next; + + /// Pointer to the tail or previous element in a deque. + /// + /// When a DQueue is used as the sentinel node for a queue, \a previous + /// points to the tail of the queue. + struct SsxDeque* previous; + +} SsxDeque; + + +typedef void (*SsxTimerCallback)(void *); + +#define SSX_TIMER_CALLBACK(callback) void callback(void *) + +struct SsxTimer; + +/// The SSX timer object + +typedef struct SsxTimer { + + /// The time queue management pointers + /// + /// This pointer container is defined as the first element of the + /// structure to allow the SsxTimer to be cast to an SsxDeque and + /// vice-versa. + SsxDeque deque; + + /// The absolute timeout of the timer. + SsxTimebase timeout; + + /// The timer period + /// + /// If not 0, then this is a periodic timer and it will be automatically + /// rescheduled in absolute time from the previous timeout. + SsxInterval period; + + /// The timer callback + /// + /// For SSX thread timers used to implement Sleep and semaphore pend + /// timeouts this field is initialized to __ssx_thread_timeout(). + SsxTimerCallback callback; + + /// Private data passed to the callback. + /// + /// For SSX thread timers used to implement Sleep and semaphore pend this + /// field is initialized to a pointer to the thread. + void *arg; + + /// Options for timer processing; See \ref ssx_timer_options + uint8_t options; + +} SsxTimer; + +/// \defgroup ssx_timer_options SSX Timer Options +/// @{ + +/// Allow interrupt preemption during the callback +/// +/// This is the normal mode for SsxTimer objects scheduled by SSX kernal +/// mechanisms. The timer callbacks effectively run as if inside a +/// highest-priority thread, allowing other interrupts to preempt them. +#define SSX_TIMER_CALLBACK_PREEMPTIBLE 0x1 + +/// @} + + +// Threads + +typedef void (*SsxThreadRoutine)(void *arg); + +#define SSX_THREAD_ROUTINE(f) void f(void *arg); + +typedef struct { + + /// Stack pointer saved during context switches. Assembler code expects + /// this to always be at address offset 0 from the thread pointer. + SsxAddress saved_stack_pointer; + + /// This is 1 past the last valid byte address of the thread stack. + /// Assembler code expects this to always be at address offset (sizeof + /// SsxAddress) from the thread pointer. + SsxAddress stack_limit; + + /// This is the original base of the stack. + /// Assembler code expects this to always be at address offset 2 * (sizeof + /// SsxAddress) from the thread pointer. + SsxAddress stack_base; + + /// If the thread is blocked on a semaphore, then this is the semaphore the + /// thread is blocked on. + SsxSemaphore *semaphore; + + /// The thread priority. + SsxThreadPriority priority; + + /// The thread state; See \ref ssx_thread_states + SsxThreadState state; + + /// Thread flags; See \ref ssx_thread_flags + SsxThreadFlags flags; + + /// The timer structure handles Sleep and blocking on a semaphore with + /// timeout. + SsxTimer timer; + +} SsxThread; + + +// Initialization APIs + +int +ssx_initialize(SsxAddress noncritical_stack, + size_t noncritical_stack_size, + SsxAddress critical_stack, + size_t critical_stack_size, + SsxTimebase initial_timebase, + uint32_t timebase_frequency_hz); + + +// Timebase APIs + +SsxTimebase +ssx_timebase_get(void); + +void +ssx_timebase_set(SsxTimebase timebase); + +// Interrupt preemption APIs + +int +ssx_interrupt_preemption_enable(void); + +int +ssx_interrupt_preemption_disable(void); + +// Timer APIs + +int +ssx_timer_create(SsxTimer *timer, + SsxTimerCallback callback, + void *arg); + +int +ssx_timer_create_nonpreemptible(SsxTimer *timer, + SsxTimerCallback callback, + void *arg); + +int +ssx_timer_schedule_absolute(SsxTimer *timer, + SsxTimebase time, + SsxInterval period); + +int +ssx_timer_schedule(SsxTimer *timer, + SsxInterval interval, + SsxInterval period); + +int +ssx_timer_cancel(SsxTimer *timer); + +int +ssx_timer_info_get(SsxTimer *timer, + SsxTimebase *timeout, + int *active); + +// Thread APIs + +int +ssx_thread_create(SsxThread *thread, + SsxThreadRoutine thread_routine, + void *arg, + SsxAddress stack, + size_t stack_size, + SsxThreadPriority priority); + +int +ssx_start_threads(void); + +int +ssx_thread_resume(SsxThread *thread); + +int +ssx_thread_suspend(SsxThread *thread); + +int +ssx_thread_delete(SsxThread *thread); + +int +ssx_complete(void); + +int +ssx_sleep_absolute(SsxTimebase time); + +int +ssx_sleep(SsxInterval interval); + +int +ssx_thread_info_get(SsxThread *thread, + SsxThreadState *state, + SsxThreadPriority *priority, + int *runnable); + +int +ssx_thread_priority_change(SsxThread *thread, + SsxThreadPriority new_priority, + SsxThreadPriority *old_priority); + +int +ssx_thread_at_priority(SsxThreadPriority priority, + SsxThread **thread); + +int +ssx_thread_priority_swap(SsxThread* thread_a, SsxThread* thread_b); + + +// Semaphore APIs + +int +ssx_semaphore_create(SsxSemaphore *semaphore, + SsxSemaphoreCount initial_count, + SsxSemaphoreCount max_count); + +int +ssx_semaphore_post(SsxSemaphore *semaphore); + +int +ssx_semaphore_pend(SsxSemaphore *semaphore, + SsxInterval timeout); + +int +ssx_semaphore_release_all(SsxSemaphore *semaphore); + + +int +ssx_semaphore_info_get(SsxSemaphore *semaphore, + SsxSemaphoreCount *count, + int *pending); + +void +ssx_semaphore_post_handler(void *arg, + SsxIrqId irq, + int priority); + +// Misc. APIs + +void +ssx_halt() __attribute__ ((noreturn)); + +// Deque APIs + +int +ssx_deque_sentinel_create(SsxDeque *deque); + +int +ssx_deque_element_create(SsxDeque *element); + + +/// Check for an empty SsxDeque +/// +/// \param deque The sentinel node of a deque +/// +/// \retval 0 The SsxDeque is not empty +/// +/// \retval 1 The SsxDeque is empty + +static inline int +ssx_deque_is_empty(SsxDeque *deque) +{ + return (deque == deque->next); +} + + +/// Check if an SsxDeque element is currently enqueued +/// +/// \param element Typically the SsxDeque object of a queable structure +/// +/// \retval 0 The element is not currently enqueued +/// +/// \retval 1 The element is currently enqueued + +static inline int +ssx_deque_is_queued(SsxDeque *element) +{ + return (element->next != 0); +} + + +/// Append an element to the tail of a deque (FIFO order) +/// +/// \param deque The sentinel node of a deque +/// +/// \param element Typically the SsxDeque object of a queable structure +/// +/// It is an error to call this API on an element that is already enqueued, +/// but the API does not check for this error. + +static inline void +ssx_deque_push_back(SsxDeque *deque, SsxDeque *element) +{ + deque->previous->next = element; + element->previous = deque->previous; + element->next = deque; + deque->previous = element; +} + + +/// Push an element at the head of a deque (LIFO order) +/// +/// \param deque The sentinel node of a deque +/// +/// \param element Typically the SsxDeque object of a queable structure +/// +/// It is an error to call this API on an element that is already enqueued, +/// but the API does not check for this error. + +static inline void +ssx_deque_push_front(SsxDeque *deque, SsxDeque *element) +{ + deque->next->previous = element; + element->next = deque->next; + element->previous = deque; + deque->next = element; +} + +/// Pop an element from the head of a deque +/// +/// \param deque The sentinel node of a deque +/// +/// \retval 0 The SsxDeque was empty prior to the call +/// +/// \retval non-0 A pointer to the previous head of the deque, which has been +/// removed from the deque and marked as no longer queued. + +// The cast of 'head' is used to remove the 'volatile' attribute. + +static inline SsxDeque * +ssx_deque_pop_front(SsxDeque *deque) +{ + SsxDeque *head; + + if (ssx_deque_is_empty(deque)) { + return 0; + } else { + head = (SsxDeque *)(deque->next); + deque->next = head->next; + deque->next->previous = deque; + head->next = 0; + return head; + } +} + + +/// Remove a deque element from any position in the deque +/// +/// \param element Typically the SsxDeque object of a queable structure +/// +/// It is an error to call this API on an element that is not currently +/// enqueued, but the API does not check for this error. + +static inline void +ssx_deque_delete(SsxDeque *element) +{ + element->previous->next = element->next; + element->next->previous = element->previous; + element->next = 0; +} + + +/// Cast a pointer to another type, in a way that won't cause warnings + +#define SSX_CAST_POINTER(t, p) ((t)((SsxAddress)(p))) + + +/// \page ssx_errors SSX API and Kernel Error Handling +/// +/// Error checking in the SSX API consumes a significant amount of code space. +/// Approximately 20% of the object code in the PPC405 port is devoted to +/// error checking. Presumably a like amount of time overhead is also added to +/// SSX API calls by this checking. +/// +/// API error checking can be disabled to save space and time in the kernel. +/// API errors can also be configured to cause kernel panics, allowing +/// applications to be coded without the overhead of error checking but still +/// providing an escape in the event of application errors or (unlikely) +/// hardware failures. The SSX default is to check for API errors and kernel +/// invariants, and panic should errors occur. +/// +/// SSX follows the Unix convention that a successful call of an API returns 0 +/// (SSX_OK), but returns a negative code in the event of failure, or to +/// provide further information. The error codes are all defined as manifest +/// constants. +/// +/// Some negative codes returned by SSX APIs are not considered errors. These +/// conditions are always checked, never cause a panic if they occur, and +/// their interpretation is always left to the application. See the detailed +/// documentation for each API for lists of error and non-error codes returned +/// by the API. +/// +/// There are three configuration options that control error handling in the +/// SSX API and kernel: +/// +/// \c SSX_ERROR_CHECK_API +/// +/// \arg \b 0 - No SSX API error checking. APIs that potentially return error +/// codes will always return 0 (SSX_OK) instead of an error code. Those +/// APIs that return negative codes that are not errors (see Table 1.5) +/// always return the negative non-error codes when appropriate. +/// +/// \arg \b 1 - (Default) All SSX API errors are checked. The behavior in +/// the event of an error is defined by the configuration option +/// SSX_ERROR_PANIC. +/// +/// \c SSX_ERROR_CHECK_KERNEL +/// +/// \arg \b 0 - No kernel invariant error checking is done. +/// +/// \arg \b 1 - (Default) Selected kernel invariants are checked. The overhead +/// for these checks should be minimal. +/// +/// \c SSX_ERROR_PANIC +/// +/// \arg \b 0 - SSX API calls return negative error codes in the event of +/// errors. Note that SSX kernel invariants always cause a panic if +/// violations occur. +/// +/// \arg \b 1 - (Default) In the event of errors SSX APIs invoke SSX_PANIC(code), +/// where code is a positive error code. Kernel invariant checks always +/// cause a panic if violations are detected. + +#endif /* __ASSEMBLER__ */ + +#endif /* __SSX_API_H__ */ diff --git a/src/ssx/ssx/ssx_core.c b/src/ssx/ssx/ssx_core.c new file mode 100755 index 0000000..7fa484a --- /dev/null +++ b/src/ssx/ssx/ssx_core.c @@ -0,0 +1,81 @@ +// $Id: ssx_core.c,v 1.2 2014/02/03 01:30:44 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssx_core.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_core.c +/// \brief Core routines for the SSX kernel. +/// +/// The entry points in this file are routines that are expected to be needed +/// at runtime by all SSX applications. This file also serves as a place for +/// kernel global variables to be realized. + +#define __SSX_CORE_C__ + +#include "ssx.h" + +#if !SSX_TIMER_SUPPORT + +/// If there is no timer support, then any call of the timer interrupt handler +/// is considered a fatal error. + +void +__ssx_timer_handler() +{ + SSX_PANIC(SSX_NO_TIMER_SUPPORT); +} + +#endif /* SSX_TIMER_SUPPORT */ + + +/// Initialize an SsxDeque sentinel node +/// +/// \param deque The sentinel node of the deque +/// +/// SSX has no way of knowing whether the \a deque is currently in use, so +/// this API must only be called on unitialized or otherwise unused sentinel +/// nodes. +/// +/// \retval 0 success +/// +/// \retval -SSX_INVALID_DEQUE_SENTINEL The \a deque pointer was null + +int +ssx_deque_sentinel_create(SsxDeque *deque) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(deque == 0, SSX_INVALID_DEQUE_SENTINEL); + } + + deque->next = deque->previous = deque; + return 0; +} + + +/// Initialize an SsxDeque element +/// +/// \param element Typically the SsxDeque object of a queable structure +/// +/// SSX has no way of knowing whether the \a element is currently in use, so +/// this API must only be called on unitialized or otherwise unused deque +/// elements. +/// +/// \retval 0 success +/// +/// \retval -SSX_INVALID_DEQUE_ELEMENT The \a element pointer was null + +int +ssx_deque_element_create(SsxDeque *element) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(element == 0, SSX_INVALID_DEQUE_ELEMENT); + } + + element->next = 0; + return 0; +} + +#undef __SSX_CORE_C__ diff --git a/src/ssx/ssx/ssx_init.c b/src/ssx/ssx/ssx_init.c new file mode 100755 index 0000000..fc12a9b --- /dev/null +++ b/src/ssx/ssx/ssx_init.c @@ -0,0 +1,159 @@ +// $Id: ssx_init.c,v 1.2 2014/02/03 01:30:44 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssx_init.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_init.c +/// \brief SSX initialization +/// +/// The entry points in this file are initialization routines - they are never +/// needed after SSX initialization and their code space could be reclaimed by +/// the application after initialization if required. + +#include "ssx.h" + +uint32_t __ssx_timebase_frequency_hz; +uint32_t __ssx_timebase_frequency_khz; +uint32_t __ssx_timebase_frequency_mhz; + + +/// Initialize SSX. +/// +/// \param noncritical_stack A stack area for noncritical interrupt handlers. +/// +/// \param noncritical_stack_size The size (in bytes) of the stack area for +/// noncritical interrupt handlers. +/// +/// \param critical_stack A stack area for critical interrupt handlers. +/// +/// \param critical_stack_size The size (in bytes) of the stack area for +/// critical interrupt handlers. +/// +/// \param initial_timebase The initial value of the SSX timebase. If this +/// argument is given as the special value \c SSX_TIMEBASE_CONTINUE, then the +/// timebase is not reset. +/// +/// \param timebase_frequency_hz The frequency of the SSX timebase in Hz. +/// +/// This routine \e must be called before any other SSX / routines, and \e +/// should be called before any interrupts are enabled. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_INVALID_ARGUMENT_INIT A stack pointer is 0 or is given +/// a 0 size. +/// +/// \retval -SSX_STACK_OVERFLOW One or both stacks are not large enough to +/// support a minimum context save in the event of an interrupt. + +// Note that SSX does not rely on any static initialization of dynamic +// variables. In debugging sessions using RAM-resident SSX images it is +// assumed that the processor may be reset at any time, so we always need to +// reset everything at initialization. + +int +ssx_initialize(SsxAddress noncritical_stack, + size_t noncritical_stack_size, + SsxAddress critical_stack, + size_t critical_stack_size, + SsxTimebase initial_timebase, + uint32_t timebase_frequency_hz) +{ + int rc; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((noncritical_stack == 0) || + (noncritical_stack_size == 0) || + (critical_stack == 0) || + (critical_stack_size == 0), + SSX_INVALID_ARGUMENT_INIT); + } + + if (initial_timebase != SSX_TIMEBASE_CONTINUES) { + __ssx_timebase_set(initial_timebase); + } + + __ssx_timebase_frequency_hz = timebase_frequency_hz; + __ssx_timebase_frequency_khz = timebase_frequency_hz / 1000; + __ssx_timebase_frequency_mhz = timebase_frequency_hz / 1000000; + + __ssx_thread_machine_context_default = SSX_THREAD_MACHINE_CONTEXT_DEFAULT; + + rc = __ssx_stack_init(&noncritical_stack, &noncritical_stack_size); + if (rc) { + return rc; + } + + __ssx_noncritical_stack = noncritical_stack; + __ssx_noncritical_stack_size = noncritical_stack_size; + + rc = __ssx_stack_init(&critical_stack, &critical_stack_size); + if (rc) { + return rc; + } + + __ssx_critical_stack = critical_stack; + __ssx_critical_stack_size = critical_stack_size; + +#if SSX_TIMER_SUPPORT + + // Initialize the time queue sentinel as a circular queue, set the next + // timeout and clear the cursor. + + ssx_deque_sentinel_create((SsxDeque*)&__ssx_time_queue); + __ssx_time_queue.cursor = 0; + __ssx_time_queue.next_timeout = SSX_TIMEBASE_MAX; + +#endif /* SSX_TIMER_SUPPORT */ + +#if SSX_THREAD_SUPPORT + + // Clear the priority map. The final entry [SSX_THREADS] is for the idle + // thread. + + int i; + for (i = 0; i <= SSX_THREADS; i++) { + __ssx_priority_map[i] = 0; + } + + // Initialize the thread scheduler + + __ssx_thread_queue_clear(&__ssx_run_queue); + __ssx_current_thread = 0; + __ssx_next_thread = 0; + __ssx_delayed_switch = 0; + +#endif /* SSX_THREAD_SUPPORT */ + + return SSX_OK; +} + + +/// Call the application main() +/// +/// __ssx_main() is called from the bootloader. It's only purpose is to +/// provide a place for the SSX_MAIN_HOOK to be called before main() is +/// called. + +void +__ssx_main(int argc, char **argv) +{ + SSX_MAIN_HOOK; + + int main(int argc, char **argv); + main(argc, argv); +} + + + + + + + + + diff --git a/src/ssx/ssx/ssx_kernel.h b/src/ssx/ssx/ssx_kernel.h new file mode 100755 index 0000000..c6a70ca --- /dev/null +++ b/src/ssx/ssx/ssx_kernel.h @@ -0,0 +1,281 @@ +#ifndef __SSX_KERNEL_H__ +#define __SSX_KERNEL_H__ + +// $Id: ssx_kernel.h,v 1.1.1.1 2013/12/11 21:03:27 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssx_kernel.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_kernel.h +/// \brief SSX portable kernel (non-API) data and data structures +/// +/// \todo In theory, as long as the critical section entry/exit macros use GCC +/// memory barriers, we should be able to eliminate all of the 'volatile' +/// declarations in SSX code. These have been added to the PPC405 port, so +/// we should try it. + +#ifdef __SSX_CORE_C__ +#define IF__SSX_CORE_C__(x) x +#define UNLESS__SSX_CORE_C__(x) +#else +#define IF__SSX_CORE_C__(x) +#define UNLESS__SSX_CORE_C__(x) x +#endif + +#if SSX_MINIMIZE_KERNEL_CODE_SPACE +#define IF_SSX_MINIMIZE_KERNEL_CODE_SPACE(x) x +#define UNLESS_SSX_MINIMIZE_KERNEL_CODE_SPACE(x) +#else +#define IF_SSX_MINIMIZE_KERNEL_CODE_SPACE(x) +#define UNLESS_SSX_MINIMIZE_KERNEL_CODE_SPACE(x) x +#endif + + +#ifndef __ASSEMBLER__ + +/// This is the stack pointer saved when switching from a thread or +/// non-critical interrupt context to a full-mode critical interrupt context. + +UNLESS__SSX_CORE_C__(extern) +volatile +SsxAddress __ssx_saved_sp_critical; + +/// The critical interrupt stack; constant once defined by the call of +/// ssx_initialize(). + +UNLESS__SSX_CORE_C__(extern) +volatile +SsxAddress __ssx_critical_stack; + +/// This is the stack pointer saved when switching from a thread context to a +/// full-mode non-critical interrupt context. + +UNLESS__SSX_CORE_C__(extern) +volatile +SsxAddress __ssx_saved_sp_noncritical; + +/// The non-critical interrupt stack; constant once defined by the call of +/// ssx_initialize(). + +UNLESS__SSX_CORE_C__(extern) +volatile +SsxAddress __ssx_noncritical_stack; + +/// This is the run queue - the queue of mapped runnable tasks. +UNLESS__SSX_CORE_C__(extern) +volatile +SsxThreadQueue __ssx_run_queue; + +/// This flag is set by \c __ssx_schedule() if a new highest-priority thread +/// becomes runnable during an interrupt handler. The context switch will +/// take place at the end of non-critical interrupt processing, and the +/// interrupt handling code will clear the flag. + +UNLESS__SSX_CORE_C__(extern) +volatile +int __ssx_delayed_switch; + +/// The currently running thread, or NULL (0) to indicate the idle thread +/// +/// \a __ssx_current_thread holds a pointer to the currently executing +/// thread. This pointer will be NULL (0) under the following conditions: +/// +/// - After ssx_initialize() but prior to ssx_start_threads() +/// +/// - After ssx_start_threads(), when no threads are runnable. In this case +/// the NULL (0) value indicates that the SSX idle thread is 'running'. +/// +/// - After ssx_start_threads(), when the current (non-idle) thread has +/// completed or been deleted. +/// +/// If \a __ssx_current_thread == 0 then there is no requirement to save any +/// register state on a context switch, either because the SSX idle thread has +/// no permanent context, or because any thread context on the kernel stack is +/// associated with a deleted thread. +/// +/// If \a __ssx_current_thread != 0 then \a __ssx_current_thread is a pointer +/// to the currently executing thread. In an interrupt handler \a +/// ssx_current_thread is a pointer to the thread whose context is saved on +/// the kernel stack. +UNLESS__SSX_CORE_C__(extern) +volatile +SsxThread* __ssx_current_thread; + +/// The thread to switch to during the next context switch, or NULL (0). +/// +/// \a __ssx_next_thread is computed by __ssx_schedule(). \a +/// __ssx_next_thread holds a pointer to the thread to switch to at the next +/// context switch. In a thread context the switch happens immediately if \a +/// __ssx_next_thread == 0 or \a __ssx_next_thread != \a __ssx_current_thread. +/// In an interrupt context the check happens at the end of processing all +/// SSX_NONCRITICAL interrupts. +/// +/// \a __ssx_next_thread may be NULL (0) under the following +/// conditions: +/// +/// - After ssx_initialize() but prior to ssx_start_threads(), assuming no +/// threads have been made runnable. +/// +/// - After ssx_start_threads(), when no threads are runnable. In this case +/// the NULL (0) value indicates that the SSX idle thread is the next thread +/// to 'run'. +/// +/// If \a __ssx_next_thread == 0 then there is no requirement to restore +/// any register state on a context switch, because the SSX idle thread has +/// no permanent context. +/// +/// If \a __ssx_next_thread != 0 then \a __ssx_next_thread is a pointer +/// to the thread whose context will be restored at the next context switch. +UNLESS__SSX_CORE_C__(extern) +volatile +SsxThread* __ssx_next_thread; + +/// The priority of \a __ssx_next_thread +/// +/// If \a __ssx_next_thread == 0, the \a __ssx_next_priority == SSX_THREADS. +UNLESS__SSX_CORE_C__(extern) +volatile +SsxThreadPriority __ssx_next_priority; + +/// This variable holds the default thread machine context for newly created +/// threads. The idle thread also uses this context. This variable is normally +/// constant after the call of \c ssx_initialize(). + +UNLESS__SSX_CORE_C__(extern) +volatile +SsxMachineContext __ssx_thread_machine_context_default; + + +/// The size of the noncritical stack (bytes). + +UNLESS__SSX_CORE_C__(extern) +volatile +size_t __ssx_noncritical_stack_size; + +/// The size of the critical stack (bytes). + +UNLESS__SSX_CORE_C__(extern) +volatile +size_t __ssx_critical_stack_size; + +/// This table maps priorities to threads, and contains SSX_THREADS + 1 +/// entries. The final entry is for the idle thread and will always be null +/// after initizlization. + +UNLESS__SSX_CORE_C__(extern) +volatile +SsxThread* __ssx_priority_map[SSX_THREADS + 1]; + +/// The SSX time queue structure +/// +/// This structure is defined for use by the kernel, however applications +/// could also use this structure to define their own time queues. + +typedef struct { + + /// A sentinel node for the time queue. + /// + /// The time queue is an SsxDeque managed as a FIFO queue for queue + /// management purpose, although events time out in time order. + /// + /// This pointer container is defined as the first element of the + /// structure to allow the SsxTimeQueue to be cast to an SsxDeque. + SsxDeque queue; + + /// The next timeout in absolute time. + SsxTimebase next_timeout; + + /// A pointer to allow preemption of time queue processing + /// + /// If non-0, then this is the next timer in the time queue to handle, or + /// a pointer to the \a queue object indicating no more timers to handle. + /// + /// \a cursor != 0 implies that time queue handler is in the midst of + /// processing the time queue, but has enabled interrupt preemption for + /// processing a timer handler. This means that 1) if the timer pointed to + /// by \a cursor is deleted then the cursor must be assigned to the + /// next timer in the queue; and 2) if a new timer is scheduled then + /// activating the next timeout will be handled by the timer handler. + SsxDeque* cursor; + +} SsxTimeQueue; + +UNLESS__SSX_CORE_C__(extern) +SsxTimeQueue __ssx_time_queue; + +/// Return a pointer to the SsxThread object of the currently running thread, +/// or NULL (0) if SSX is idle or has not been started. +/// +/// In this API the current thread is not volatile - it will never change +/// inside application code - thus the 'volatile' is cast away. The SSX kernel +/// does not (must not) use this API. + +UNLESS__SSX_CORE_C__(extern) +inline SsxThread * +ssx_current(void) +{ + return (SsxThread *)__ssx_current_thread; +} + + +/// Set the timebase. This is only called at initialization. Machine +/// specific. + +void +__ssx_timebase_set(SsxTimebase t); + +/// Schedule the next timeout in a machine-specific way. + +void +__ssx_schedule_hardware_timeout(SsxTimebase timeout); + +/// Cancel the next timeout in a machine-specific way. + +void +__ssx_cancel_hardware_timeout(void); + +/// The thread timeout handler. Portable. + +SSX_TIMER_CALLBACK(__ssx_thread_timeout); + +/// Generic stack initialization. Portable. + +int +__ssx_stack_init(SsxAddress *stack, + size_t *size); + +/// Machine-specific thread context initialization. + +void +__ssx_thread_context_initialize(SsxThread *thread, + SsxThreadRoutine thread_routine, + void *arg); + +/// Machine specific resumption of __ssx_next_thread at __ssx_next_priority +/// without saving the current context. +void +__ssx_next_thread_resume(void); + +/// Schedule a timer in the time queue. Portable. +void +__ssx_timer_schedule(SsxTimer *timer); + +/// Remove a timer from the time queue. Portable. +int +__ssx_timer_cancel(SsxTimer *timer); + +void +__ssx_schedule(void); + + +// Call the application main(). Portable. + +void +__ssx_main(int argc, char **argv); + +#endif /* __ASSEMBLER__ */ + +#endif /* __SSX_KERNEL_H__ */ diff --git a/src/ssx/ssx/ssx_macros.h b/src/ssx/ssx/ssx_macros.h new file mode 100755 index 0000000..76d3ba7 --- /dev/null +++ b/src/ssx/ssx/ssx_macros.h @@ -0,0 +1,119 @@ +#ifndef __SSX_MACROS_H__ +#define __SSX_MACROS_H__ + +// $Id: ssx_macros.h,v 1.2 2014/02/03 01:30:44 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssx_macros.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_macros.h +/// \brief Boilerplate macros for SSX + +/// This macro encapsulates error handling boilerplate for code that uses the +/// SSX API-type error handling, for errors that do not occur in critical +/// sections. + +#define SSX_ERROR(code) \ + do { \ + if (SSX_ERROR_PANIC) { \ + SSX_PANIC(code); \ + } else { \ + return -(code); \ + } \ + } while (0) + + +/// This macro encapsulates error handling boilerplate in the SSX API +/// functions, for errors that do not occur in critical sections. + +#define SSX_ERROR_IF(condition, code) \ + do { \ + if (condition) { \ + SSX_ERROR(code); \ + } \ + } while (0) + + +/// This macro encapsulates error handling boilerplate in the SSX API +/// functions, for errors that do not occur in critical sections and always +/// force a kernel panic, indicating a kernel or API bug. + +#define SSX_PANIC_IF(condition, code) \ + do { \ + if (condition) { \ + SSX_PANIC(code); \ + } \ + } while (0) + + +/// This macro encapsulates error handling boilerplate in the SSX API +/// functions, for errors that do not occur in critical sections. +/// The error handling will only be enabled when SSX_ERROR_CHECK_API +/// is enabled. + +#define SSX_ERROR_IF_CHECK_API(condition, code) \ + do { \ + if (SSX_ERROR_CHECK_API) { \ + SSX_ERROR_IF(condition, code); \ + } \ + } while (0) + +/// This macro encapsulates error handling boilerplate in the SSX API +/// functions, for errors that occur in critical sections. + +#define SSX_ERROR_IF_CRITICAL(condition, code, context) \ + do { \ + if (condition) { \ + if (SSX_ERROR_PANIC) { \ + SSX_PANIC(code); \ + ssx_critical_section_exit(context); \ + } else { \ + ssx_critical_section_exit(context); \ + return -(code); \ + } \ + } \ + } while (0) + + +/// This is a general macro for errors that require cleanup before returning +/// the error code. + +#define SSX_ERROR_IF_CLEANUP(condition, code, cleanup) \ + do { \ + if (condition) { \ + if (SSX_ERROR_PANIC) { \ + SSX_PANIC(code); \ + cleanup; \ + } else { \ + cleanup; \ + return -(code); \ + } \ + } \ + } while (0) + + +/// Most SSX APIs can not be called from critical interrupt contexts. + +#define SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT() \ + SSX_ERROR_IF(__ssx_kernel_context_critical_interrupt(), \ + SSX_ILLEGAL_CONTEXT_CRITICAL_INTERRUPT) + + +/// Some SSX APIs can only be called from thread contexts - these are APIs +/// that threads call on 'themselves'. + +#define SSX_ERROR_UNLESS_THREAD_CONTEXT() \ + SSX_ERROR_IF(!__ssx_kernel_context_thread(), \ + SSX_ILLEGAL_CONTEXT_THREAD_CONTEXT) + + +/// Some SSX APIs must be called from an interrupt context only. + +#define SSX_ERROR_UNLESS_ANY_INTERRUPT_CONTEXT() \ + SSX_ERROR_IF(!__ssx_kernel_context_any_interrupt(), \ + SSX_ILLEGAL_CONTEXT_INTERRUPT_CONTEXT) + +#endif /* __SSX_MACROS_H__ */ diff --git a/src/ssx/ssx/ssx_semaphore_core.c b/src/ssx/ssx/ssx_semaphore_core.c new file mode 100755 index 0000000..f1f64e8 --- /dev/null +++ b/src/ssx/ssx/ssx_semaphore_core.c @@ -0,0 +1,331 @@ +// $Id: ssx_semaphore_core.c,v 1.2 2014/02/03 01:30:44 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssx_semaphore_core.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_semaphore_core.c +/// \brief SSX semaphore APIs +/// +/// The entry points in this file are considered 'core' routines that will +/// always be present at runtime in any SSX application that enables +/// semaphores. + +#include "ssx.h" + +/// Post a count to a semaphore +/// +/// \param semaphore A pointer to the semaphore +/// +/// If any thread is pending on the semaphore, the highest priority thread +/// will be made runnable and the internal count will remain 0. +/// +/// If no thread is pending on the semaphore then the internal count will be +/// incremented by 1, with overflow wrapping the internal count through 0. If +/// the \a max_count argument supplied when the semaphore was created is +/// non-zero and the new internal count is greater than the \a max_count, an +/// overflow error will be signalled. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical interrupt +/// context. +/// +/// \retval -SSX_INVALID_SEMAPHORE_AT_POST The \a semaphore is a null (0) pointer. +/// +/// \retval -SSX_SEMAPHORE_OVERFLOW The \a max_count argument supplied when +/// the semaphore was created is non-zero and the new internal count is +/// greater than the \a max_count. + +int +ssx_semaphore_post(SsxSemaphore *semaphore) +{ + SsxMachineContext ctx; + SsxThreadPriority priority; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF(semaphore == 0, SSX_INVALID_SEMAPHORE_AT_POST); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + priority = __ssx_thread_queue_min(&(semaphore->pending_threads)); + + if (priority != SSX_IDLE_THREAD_PRIORITY) { + + __ssx_thread_queue_delete(&(semaphore->pending_threads), priority); + __ssx_thread_queue_insert(&__ssx_run_queue, priority); + + SSX_TRACE_THREAD_SEMAPHORE_POST(priority); + + __ssx_schedule(); + + } else { + + semaphore->count++; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((semaphore->max_count > 0) && + (semaphore->count > semaphore->max_count), + SSX_SEMAPHORE_OVERFLOW); + } + } + + ssx_critical_section_exit(&ctx); + + return SSX_OK; +} + + +/// Pend on a semaphore with timeout +/// +/// \param semaphore A pointer to the semaphore +/// +/// \param timeout A relative timeout in SSX timebase ticks, including the +/// special values SSX_NO_WAIT and SSX_WAIT_FOREVER +/// +/// This API is normally called from threads, and can only be successfully +/// called from interupt handlers under special conditions. +/// +/// If the internal count of the \a semaphore is non-zero, the internal count +/// is decremented by one and execution of the caller continues. +/// +/// If the internal count of the \a semaphore is zero and the \a timeout is +/// SSX_NO_WAIT (0) then the call returns immediately with the informational +/// code -SSX_SEMAPHORE_PEND_NO_WAIT. +/// +/// If the internal count of the \a semaphore is zero and the \a timeout is +/// non-zero then a thread will block until either a semaphore count is +/// acquired or the relative timeout expires. If this condition occurs in a +/// call from an interrupt context or before threads have been started then +/// the call will fail with the error \c -SSX_SEMAPHORE_PEND_WOULD_BLOCK. +/// +/// Once timed out the thread is removed from the semaphore pending queue and +/// made runnable, and the ssx_semaphore_pend() operation will fail, even if +/// the semaphore count becomes available before the thread runs again. The +/// ssx_semaphore_pend() API returns the informational code +/// -SSX_SEMAPHORE_PEND_TIMED_OUT in this case. +/// +/// By convention, a timeout interval equal to the maximum possible value of +/// the \c SsxInterval type is taken to mean "wait forever". A thread blocked +/// on a semaphore in this mode will never time out. SSX provides this +/// constant as \c SSX_WAIT_FOREVER. +/// +/// Return values other than SSX_OK (0) are not necessarily errors; see \ref +/// ssx_errors +/// +/// The following return codes are non-error codes: +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_SEMAPHORE_PEND_NO_WAIT timeout is set to SSX_NO_WAIT +/// +/// \retval -SSX_SEMAPHORE_PEND_TIMED_OUT The semaphore was not acquired +/// before the timeout expired. +/// +/// The following return codes are error codes: +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical interrupt +/// context. +/// +/// \retval -SSX_INVALID_SEMAPHORE_AT_PEND The \a semaphore is a null (0) +/// pointer. +/// +/// \retval -SSX_SEMAPHORE_PEND_WOULD_BLOCK The call was made from an +/// interrupt context (or before threads have been started), the semaphore +/// internal count was 0 and a non-zero timeout was specified. + +// Note: Casting __ssx_current_thread removes the 'volatile' attribute. + +int +ssx_semaphore_pend(SsxSemaphore *semaphore, + SsxInterval timeout) +{ + SsxMachineContext ctx; + SsxThreadPriority priority; + SsxThread *thread; + SsxTimer *timer = 0; + int rc = SSX_OK; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF(semaphore == 0, SSX_INVALID_SEMAPHORE_AT_PEND); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + if (semaphore->count != 0) { + + semaphore->count--; + + } else if (timeout == SSX_NO_WAIT) { + + rc = -SSX_SEMAPHORE_PEND_NO_WAIT; + + } else { + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL(!__ssx_kernel_context_thread(), + SSX_SEMAPHORE_PEND_WOULD_BLOCK, + &ctx); + } + + thread = (SsxThread *)__ssx_current_thread; + priority = thread->priority; + + __ssx_thread_queue_insert(&(semaphore->pending_threads), priority); + + thread->semaphore = semaphore; + thread->flags |= SSX_THREAD_FLAG_SEMAPHORE_PEND; + + SSX_TRACE_THREAD_SEMAPHORE_PEND(priority); + + if (timeout != SSX_WAIT_FOREVER) { + timer = &(thread->timer); + timer->timeout = ssx_timebase_get() + timeout; + __ssx_timer_schedule(timer); + thread->flags |= SSX_THREAD_FLAG_TIMER_PEND; + } + + __ssx_thread_queue_delete(&__ssx_run_queue, priority); + __ssx_schedule(); + + thread->flags &= ~SSX_THREAD_FLAG_SEMAPHORE_PEND; + + if (thread->flags & SSX_THREAD_FLAG_TIMER_PEND) { + if (thread->flags & SSX_THREAD_FLAG_TIMED_OUT) { + rc = -SSX_SEMAPHORE_PEND_TIMED_OUT; + } else { + __ssx_timer_cancel(timer); + } + thread->flags &= + ~(SSX_THREAD_FLAG_TIMER_PEND | SSX_THREAD_FLAG_TIMED_OUT); + } + } + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +/// Release all threads blocked on a semaphore +/// +/// \param semaphore A pointer to a semaphore +/// +/// This API is provided to allow an SSX semaphore to be used as a thread +/// barrier. ssx_semaphore_release_all() simultaneously unblocks all threads +/// (if any) currently pending on a semaphore. A semaphore to be used as a +/// thread barrier will typically be initialized with +/// ssx_semaphore_create(\a sem, 0, 0), and sxx_semaphore_post() would never be +/// called on the \a sem. +/// +/// This API never modifies the \a count field of the semaphore; If any +/// threads are blocked on a semaphore the semaphore count is 0 by definition. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical interrupt +/// context. +/// +/// \retval -SSX_INVALID_SEMAPHORE_AT_RELEASE The \a semaphore is a null (0) +/// pointer. + +int +ssx_semaphore_release_all(SsxSemaphore* semaphore) +{ + SsxMachineContext ctx; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF(semaphore == 0, SSX_INVALID_SEMAPHORE_AT_RELEASE); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + __ssx_thread_queue_union(&__ssx_run_queue, &(semaphore->pending_threads)); + __ssx_thread_queue_clear(&(semaphore->pending_threads)); + __ssx_schedule(); + + ssx_critical_section_exit(&ctx); + + return SSX_OK; +} + + +/// Get information about a semaphore. +/// +/// \param semaphore A pointer to the SsxSemaphore to query +/// +/// \param count The value returned through this pointer is the current count +/// of the semaphore. The caller can set this parameter to the null pointer +/// (0) if this information is not required. +/// +/// \param pending The value returned through this pointer is the current +/// number of threads pending on the semaphore. The caller can set this +/// parameter to the null pointer (0) if this information is not required. +/// +/// The information returned by this API can only be guaranteed consistent if +/// the API is called from an SSX_NONCRITICAL critical section. Since the +/// implementation of this API does not require a critical section, it is not +/// an error to call this API from a critical interrupt context. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_INVALID_SEMAPHORE_AT_INFO The \a semaphore is a null (0) +/// pointer. + +int +ssx_semaphore_info_get(SsxSemaphore* semaphore, + SsxSemaphoreCount* count, + int* pending) + +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(semaphore == 0, SSX_INVALID_SEMAPHORE_AT_INFO); + } + + if (count) { + *count = semaphore->count; + } + if (pending) { + *pending = __ssx_thread_queue_count(&(semaphore->pending_threads)); + } + + return SSX_OK; +} + + +/// An simple interrupt handler that posts to a semaphore. +/// +/// To implement basic event-driven blocking of a thread, install +/// ssx_semaphore_post_handler() as the handler for a non-critical interrupt +/// and provide a pointer to the semaphore as the \a arg argument in +/// ssx_irq_handler_set(). The semaphore should be initialized with +/// ssx_semaphore_create(&sem, 0, 1). This handler simply disables (masks) +/// the interrupt, clears the status and calls ssx_semaphore_post() on the +/// semaphore. +/// +/// Note that clearing the status in the interrupt controller as done here is +/// effectively a no-op for level-sensitive interrupts. In the level-sensitive +/// case any thread pending on the semaphore must reset the interrupt +/// condition in the device before re-enabling the interrupt. + +void +ssx_semaphore_post_handler_full(void *arg, SsxIrqId irq, int priority) +{ + ssx_irq_disable(irq); + ssx_irq_status_clear(irq); + ssx_semaphore_post((SsxSemaphore *)arg); +} + +SSX_IRQ_FAST2FULL(ssx_semaphore_post_handler, ssx_semaphore_post_handler_full); diff --git a/src/ssx/ssx/ssx_semaphore_init.c b/src/ssx/ssx/ssx_semaphore_init.c new file mode 100755 index 0000000..98bba89 --- /dev/null +++ b/src/ssx/ssx/ssx_semaphore_init.c @@ -0,0 +1,84 @@ +// $Id: ssx_semaphore_init.c,v 1.2 2014/02/03 01:30:44 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssx_semaphore_init.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_semaphore_init.c +/// \brief SSX semaphore API initialization routines +/// +/// The entry points in this file are routines that are typically used during +/// initialization, and their code space could be deallocated and recovered if +/// no longer needed by the application after initialization. + +#include "ssx.h" + +/// Create (initialize) a semaphore +/// +/// \param semaphore A pointer to an SsxSemaphore structure to initialize +/// +/// \param initial_count The initial count of the semaphore +/// +/// \param max_count The maximum count allowed in the semaphore, for error +/// checking +/// +/// Semaphores are created (initialized) by a call of \c +/// ssx_semaphore_create(), using an application-provided instance of an \c +/// SsxSemaphore structure. This structure \e is the semaphore, so the +/// application must never modify the structure if the semaphore is in use. +/// SSX has no way to know if an \c SsxSemaphore structure provided to +/// \c ssx_semaphore_create() is safe to use as a semaphore, and will silently +/// modify whatever memory is provided. +/// +/// SSX provides two simple overflow semantics based on the value of max_count +/// in the call of \c ssx_semaphore_create(). +/// +/// If \a max_count = 0, then posting to the semaphore first increments the +/// internal count by 1. Overflows are ignored and will wrap the internal +/// count through 0. +/// +/// If \a max_count != 0, then posting to the semaphore first increments the +/// internal count by 1, wrapping through 0 in the event of overflow. If the +/// resulting count is greater than max_count, \c ssx_semaphore_post() will +/// return the error \c -SSX_SEMAPHORE_POST_OVERFLOW to the caller. +/// +/// In most applications it is probably best to use the \a max_count != 0 +/// semantics to trap programming errors, unless there is a specific +/// application where overflow is expected and ignorable. As a fine point of +/// the specification, a \a max_count of 0 is equivalent to a max_count of +/// 0xFFFFFFFF. +/// +/// Return values other then SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_INVALID_SEMAPHORE_AT_CREATE The \a semaphore is a null (0) +/// pointer. +/// +/// \retval -SSX_INVALID_ARGUMENT_SEMAPHORE The \a max_count is non-zero +/// and less than the \a initial_count. + +int +ssx_semaphore_create(SsxSemaphore *semaphore, + SsxSemaphoreCount initial_count, + SsxSemaphoreCount max_count) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(semaphore == 0, SSX_INVALID_SEMAPHORE_AT_CREATE); + SSX_ERROR_IF((max_count != 0) && (initial_count > max_count), + SSX_INVALID_ARGUMENT_SEMAPHORE); + } + + __ssx_thread_queue_clear(&(semaphore->pending_threads)); + semaphore->count = initial_count; + semaphore->max_count = max_count; + + return SSX_OK; +} + + + + + diff --git a/src/ssx/ssx/ssx_stack_init.c b/src/ssx/ssx/ssx_stack_init.c new file mode 100755 index 0000000..9d6331a --- /dev/null +++ b/src/ssx/ssx/ssx_stack_init.c @@ -0,0 +1,87 @@ +// $Id: ssx_stack_init.c,v 1.1.1.1 2013/12/11 21:03:28 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssx_stack_init.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_stack_init.c +/// \brief SSX stack initialization +/// +/// The entry points in this file are initialization routines - they are never +/// needed after SSX initialization and their code space could be reclaimed by +/// the application after initialization if required. +/// +/// This code was split out from "ssx_init.c" because it may be needed in a +/// thread configuration if threads are being created dynamically. in an +/// interrupt-only configuration it is not needed after \c ssx_initialize(). + +#include "ssx.h" + +/// Initialize a stack area. +/// +/// \param stack A pointer to the smallest legal address of the stack. The +/// stack address is modified as the stack is aligned and initialized. +/// +/// \param size A pointer to the size of the stack (in bytes). The size is +/// modified as the stack is aligned and initialized. At exit this is the +/// final usable stack area size aligned to the size of the SSX_STACK_TYPE. +/// +/// SSX makes no assumptions about size or alignment of the area provided as a +/// stack, and carefully aligns and initializes the stack. Regardless of how +/// the stack grows, the \a stack parameter is considered to be the lowest +/// legal address of the stack. + +int +__ssx_stack_init(SsxAddress *stack, + size_t *size) +{ + SsxAddress mask; + size_t excess, i, count; + SSX_STACK_TYPE *p; + + if (SSX_STACK_DIRECTION < 0) { + + // Stacks grow down. The initial stack pointer is set to just above + // the last allocated stack address. This is legal for pre-decrement + // stacks, otherwise the initial address is first brought into range + // before alignment. The stack is aligned downward, then the size is + // adjusted to a multiple of the stack type. Stacks are optionally + // prepatterned. Alignment is assumed to be a power of 2. + + *stack += *size; + + if (!SSX_STACK_PRE_DECREMENT) { + *stack -= sizeof(SSX_STACK_TYPE); + *size -= sizeof(SSX_STACK_TYPE); + } + + mask = SSX_STACK_ALIGNMENT - 1; + excess = *stack & mask; + *stack -= excess; + *size -= excess; + *size = (*size / sizeof(SSX_STACK_TYPE)) * sizeof(SSX_STACK_TYPE); + + if (SSX_STACK_CHECK) { + p = (SSX_STACK_TYPE *)(*stack); + count = *size / sizeof(SSX_STACK_TYPE); + for (i = 0; i < count; i++) { + if (SSX_STACK_PRE_DECREMENT) { + *(--p) = SSX_STACK_PATTERN; + } else { + *(p--) = SSX_STACK_PATTERN; + } + } + } + + __ssx_stack_create_initial_frame(stack, size); + + } else { + + SSX_PANIC(SSX_UNIMPLEMENTED); + } + + return SSX_OK; +} + diff --git a/src/ssx/ssx/ssx_thread_core.c b/src/ssx/ssx/ssx_thread_core.c new file mode 100755 index 0000000..d6124e8 --- /dev/null +++ b/src/ssx/ssx/ssx_thread_core.c @@ -0,0 +1,946 @@ +// $Id: ssx_thread_core.c,v 1.2 2014/02/03 01:30:44 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssx_thread_core.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_thread_core.c +/// \brief SSX thread APIs +/// +/// The entry points in this file are considered 'core' routines that will +/// always be present at runtime in any SSX application that enables threads. + +#include "ssx.h" + +#define __SSX_THREAD_CORE_C__ + + +// This routine is only used locally. Noncritical interrupts must be disabled +// at entry. + +static inline int +__ssx_thread_is_active(SsxThread *thread) +{ + return ((thread->state != SSX_THREAD_STATE_COMPLETED) && + (thread->state != SSX_THREAD_STATE_DELETED)); +} + + +// This routine is only used locally. Noncritical interrupts must be disabled +// at entry. + +static inline int +__ssx_thread_is_mapped(SsxThread *thread) +{ + return (thread->state == SSX_THREAD_STATE_MAPPED); +} + + +// This routine is only used locally. Noncritical interrupts must be disabled +// at entry. This is only called on mapped threads. + +static inline int +__ssx_thread_is_runnable(SsxThread *thread) +{ + return __ssx_thread_queue_member(&__ssx_run_queue, thread->priority); +} + + +// This routine is only used locally. Noncritical interrupts must be disabled +// at entry. + +static inline SsxThread* +__ssx_thread_at_priority(SsxThreadPriority priority) +{ + return (SsxThread*)__ssx_priority_map[priority]; +} + + +// This routine is only used locally. Noncritical interrupts must be disabled +// at entry. The caller must also have checked that the priority is free. +// This routine is only called on threads known to be in a suspended state, +// either SSX_THREAD_STATE_SUSPENDED_RUNNABLE or +// SSX_THREAD_STATE_SUSPENDED_BLOCKED. Mapping a runnable thread adds it to +// the run queue. Mapping a thread pending on a semaphore either takes the +// count and becomes runnable or adds the thread to the pending queue for the +// semaphore. Mapping a sleeping thread requires no further action +// here. Scheduling after the map must be handled by the caller. + +void +__ssx_thread_map(SsxThread* thread) +{ + SsxThreadPriority priority; + + priority = thread->priority; + __ssx_priority_map[priority] = thread; + + if (thread->state == SSX_THREAD_STATE_SUSPENDED_RUNNABLE) { + + __ssx_thread_queue_insert(&__ssx_run_queue, priority); + + } else if (thread->flags & SSX_THREAD_FLAG_SEMAPHORE_PEND) { + + if (thread->semaphore->count) { + + thread->semaphore->count--; + __ssx_thread_queue_insert(&__ssx_run_queue, priority); + + } else { + + __ssx_thread_queue_insert(&(thread->semaphore->pending_threads), + priority); + } + } + + thread->state = SSX_THREAD_STATE_MAPPED; + + if (SSX_KERNEL_TRACE_ENABLE) { + if (__ssx_thread_is_runnable(thread)) { + SSX_TRACE_THREAD_MAPPED_RUNNABLE(priority); + } else if (thread->flags & SSX_THREAD_FLAG_SEMAPHORE_PEND) { + SSX_TRACE_THREAD_MAPPED_SEMAPHORE_PEND(priority); + } else { + SSX_TRACE_THREAD_MAPPED_SLEEPING(priority); + } + } +} + + +// This routine is only used locally. Noncritical interrupts must be disabled +// at entry. This routine is only ever called on threads in the +// SSX_THREAD_STATE_MAPPED. Unmapping a thread removes it from the priority +// map, the run queue and any semaphore pend, but does not cancel any +// timers. Scheduling must be handled by the code calling +// __ssx_thread_unmap(). + +void +__ssx_thread_unmap(SsxThread *thread) +{ + SsxThreadPriority priority; + + priority = thread->priority; + __ssx_priority_map[priority] = 0; + + if (__ssx_thread_is_runnable(thread)) { + + thread->state = SSX_THREAD_STATE_SUSPENDED_RUNNABLE; + __ssx_thread_queue_delete(&__ssx_run_queue, priority); + + } else { + + thread->state = SSX_THREAD_STATE_SUSPENDED_BLOCKED; + if (thread->flags & SSX_THREAD_FLAG_SEMAPHORE_PEND) { + __ssx_thread_queue_delete(&(thread->semaphore->pending_threads), + priority); + } + } +} + + +// Schedule and run the highest-priority mapped runnable thread. +// +// The priority of the next thread to run is first computed. This may be +// SSX_THREADS, indicating that the only thread to run is the idle thread. +// This will always cause (or defer) a 'context switch' to the idle thread. +// Otherwise, if the new thread is not equal to the current thread this will +// also cause (or defer) a context switch. Note that scheduling is defined in +// terms of priorities but actually implemented in terms of SsxThread pointers. +// +// If we are not yet in thread mode we're done - threads will be started by +// ssx_start_threads() later. If we're in thread context a context switch +// happens immediately. In an interrupt context the switch is deferred to the +// end of SSX_NONCRITICAL interrupt processing. + +void +__ssx_schedule(void) +{ + __ssx_next_priority = __ssx_thread_queue_min(&__ssx_run_queue); + __ssx_next_thread = __ssx_priority_map[__ssx_next_priority]; + + if ((__ssx_next_thread == 0) || + (__ssx_next_thread != __ssx_current_thread)) { + + if (__ssx_kernel_mode_thread()) { + if (__ssx_kernel_context_thread()) { + if (__ssx_current_thread != 0) { + __ssx_switch(); + } else { + __ssx_next_thread_resume(); + } + } else { + __ssx_delayed_switch = 1; + } + } + } +} + + +// This routine is only used locally. +// +// Completion and deletion are pretty much the same thing. Completion is +// simply self-deletion of the current thread (which is mapped by +// definition.) The complete/delete APIs have slightly different error +// conditions but are otherwise the same. +// +// Deleting a mapped thread first unmaps (suspends) the thread, which takes +// care of removing the thread from any semaphores it may be pending on. Then +// any outstanding timer is also cancelled. +// +// If the current thread is being deleted we install the idle thread as +// __ssx_current_thread, so scheduling is forced and no context is saved on +// the context switch. +// +// Note that we do not create trace events for unmapped threads since the trace +// tag only encodes the priority, which may be in use by a mapped thread. + +void +__ssx_thread_delete(SsxThread *thread, SsxThreadState final_state) +{ + SsxMachineContext ctx; + int mapped; + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + mapped = __ssx_thread_is_mapped(thread); + + if (mapped) { + __ssx_thread_unmap(thread); + } + + __ssx_timer_cancel(&(thread->timer)); + thread->state = final_state; + + if (mapped) { + + if (SSX_KERNEL_TRACE_ENABLE) { + if (final_state == SSX_THREAD_STATE_DELETED) { + SSX_TRACE_THREAD_DELETED(thread->priority); + } else { + SSX_TRACE_THREAD_COMPLETED(thread->priority); + } + } + + if (thread == __ssx_current_thread) { + __ssx_current_thread = 0; + } + __ssx_schedule(); + } + + ssx_critical_section_exit(&ctx); +} + + +// Generic thread timeout +// +// This routine is called as a timer callback either because a sleeping thread +// has timed out or a thread pending on a semaphore has timed out. If the +// thread is not already runnable then the the timeout flag is set, and if the +// thread is mapped it is scheduled. +// +// This implementation allows that a thread blocked on a timer may have been +// made runnable by some other mechanism, such as acquiring a semaphore. In +// order to provide an iteration-free implementation of +// ssx_semaphore_release_all(), cancelling any semaphore timeouts is deferred +// until the thread runs again. +// +// __ssx_thread_timeout() is currenly the only timer interrupt called from a +// critical section. +// +// Note that we do not create trace events for unmapped threads since the trace +// tag only encodes the priority, which may be in use by a mapped thread. + +void +__ssx_thread_timeout(void *arg) +{ + SsxThread *thread = (SsxThread *)arg; + + switch (thread->state) { + + case SSX_THREAD_STATE_MAPPED: + if (!__ssx_thread_is_runnable(thread)) { + thread->flags |= SSX_THREAD_FLAG_TIMED_OUT; + __ssx_thread_queue_insert(&__ssx_run_queue, thread->priority); + __ssx_schedule(); + } + break; + + case SSX_THREAD_STATE_SUSPENDED_RUNNABLE: + break; + + case SSX_THREAD_STATE_SUSPENDED_BLOCKED: + thread->flags |= SSX_THREAD_FLAG_TIMED_OUT; + thread->state = SSX_THREAD_STATE_SUSPENDED_RUNNABLE; + break; + + default: + SSX_PANIC(SSX_THREAD_TIMEOUT_STATE); + } +} + + +// This routine serves as a container for the SSX_START_THREADS_HOOK and +// actually starts threads. The helper routine __ssx_call_ssx_start_threads() +// arranges this routine to be called with interrupts disabled while running +// on the noncritical interrupt stack. +// +// The reason for this roundabout is that we want to be able to run a hook +// routine (transparent to the application) that can hand over every last byte +// of free memory to "malloc()" - including the stack of main(). Since we +// always need to run on some stack, we chose to run the hook on the kernel +// noncritical interrupt stack. However to do this safely we need to make sure +// that no interrupts will happen during this time. When __ssx_thread_resume() +// is finally called all stack-based context is lost but it doesn't matter at +// that point - it's a one-way street into thread execution. +// +// This is considered part of ssx_start_threads() and so is also considered a +// 'core' routine. + +void +__ssx_start_threads(void) +{ + SSX_START_THREADS_HOOK; + + __ssx_next_thread_resume(); + + SSX_PANIC(SSX_START_THREADS_RETURNED); +} + + +/// Start SSX threads +/// +/// This routine starts the SSX thread scheduler infrastructure. This routine +/// must be called after a call of \c ssx_initialize(). This routine never +/// returns. Interrupt (+ timer) only configurations of SSX need not call this +/// routine. +/// +/// Note: This tiny routine is considered a 'core' routine so that the +/// initialziation code can safely recover all 'init' code space before +/// starting threads. +/// +/// This routine typically does not return - any return value indicates an +/// error; see \ref ssx_errors +/// +/// \retval -SSX_ILLEGAL_CONTEXT_THREAD The API was called twice. + +int +ssx_start_threads(void) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(__ssx_kernel_mode_thread(), SSX_ILLEGAL_CONTEXT_THREAD); + } + + __ssx_call_ssx_start_threads(); + + return 0; +} + + +/// Resume a suspended thread +/// +/// \param thread The thread to resume +/// +/// SSX only allows one thread at a time to run at a given priority, and +/// implements the notion of a thread \e claiming a priority. A suspended +/// thread claims a priority when it is mapped by a call of +/// ssx_thread_resume(). This API will succeed only if no other active thread +/// is currently mapped at the priority assigned to the thread. SSX provides +/// the ssx_thread_at_priority() API which allows an application-level +/// scheduler to correctly manage multiple threads running at the same +/// priority. +/// +/// If the thread was sleeping while suspended it remains asleep. However if +/// the sleep timer timed out while the thread was suspended it will be +/// resumed runnable. +/// +/// If the thread was blocked on a semaphore when it was suspended, then when +/// the thread is resumed it will attempt to reacquire the semaphore. +/// However, if the thread was blocked on a semaphore with timeout while +/// suspended and the timeout interval has passed, the thread will be resumed +/// runnable and see that the semaphore pend timed out. +/// +/// It is not an error to call ssx_thread_resume() on a mapped +/// thread. However it is an error to call ssx_thread_resume() on a completed +/// or deleted thread. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion, including calls on a \a thread that is +/// already mapped. +/// +/// \retval -SSX_ILLEGAL_CONTEXT_THREAD The API was called +/// from a critical interrupt context. +/// +/// \retval -SSX_INVALID_THREAD_AT_RESUME1 The \a thread is a null (0) pointer. +/// +/// \retval -SSX_INVALID_THREAD_AT_RESUME2 The \a thread is not active, +/// i.e. has completed or been deleted. +/// +/// \retval -SSX_PRIORITY_IN_USE_AT_RESUME Another thread is already mapped at +/// the priority of the \a thread. + +int +ssx_thread_resume(SsxThread *thread) +{ + SsxMachineContext ctx; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF(thread == 0, SSX_INVALID_THREAD_AT_RESUME1); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL(!__ssx_thread_is_active(thread), + SSX_INVALID_THREAD_AT_RESUME2, + &ctx); + } + + if (!__ssx_thread_is_mapped(thread)) { + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL(__ssx_priority_map[thread->priority] != 0, + SSX_PRIORITY_IN_USE_AT_RESUME, + &ctx); + } + __ssx_thread_map(thread); + __ssx_schedule(); + } + + ssx_critical_section_exit(&ctx); + + return SSX_OK; +} + + +/// Suspend a thread +/// +/// Any active thread can be suspended. A suspended thread 1) remains active +/// but will not be scheduled; 2) relinquishes its priority assignment, +/// allowing another thread to be resumed at the suspended thread's priority; +/// and 3) disassociates from any semaphore mutual exclusion it may have been +/// participating in. +/// +/// If a sleeping thread is suspended, the sleep timer remains active but a +/// timeout of the timer simply marks the thread as runnable, but does not +/// resume the thread. +/// +/// If a thread blocked on a semaphore is suspended, the thread no longer +/// participates in the semaphore mutual exclusion. If the thread is later +/// resumed it will attempt to acquire the semaphore again the next time it +/// runs (unless it was blocked with a timeout and the timeout has expired). +/// +/// If a thread blocked on a semaphore with timeout is suspended, the +/// semaphore timeout timer continues to run. If the timer times out while the +/// thread is suspended the thread is simply marked runnable. If the thread is +/// later resumed, the suspended call of \c ssx_semaphore_pend() will return the +/// timeout code -SSX_SEMAPHORE_PEND_TIMED_OUT. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion, including calls on a \a thread that is +/// already suspended. +/// +/// \retval -SSX_ILLEGAL_CONTEXT_THREAD The API was called from a critical +/// interrupt context. +/// +/// \retval -SSX_INVALID_THREAD_AT_SUSPEND1 The \a thread is a null (0) pointer +/// +/// \retval -SSX_INVALID_THREAD_AT_SUSPEND2 The \a thread is not active, +/// i.e. has completed or been deleted. + +int +ssx_thread_suspend(SsxThread *thread) +{ + SsxMachineContext ctx; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF((thread == 0), SSX_INVALID_THREAD_AT_SUSPEND1); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL(!__ssx_thread_is_active(thread), + SSX_INVALID_THREAD_AT_SUSPEND2, + &ctx); + } + + if (__ssx_thread_is_mapped(thread)) { + + SSX_TRACE_THREAD_SUSPENDED(thread->priority); + __ssx_thread_unmap(thread); + __ssx_schedule(); + } + + ssx_critical_section_exit(&ctx); + + return SSX_OK; +} + + +/// Delete a thread +/// +/// Any active thread can be deleted. If a thread is deleted it is removed +/// from the run queue, deleted from the timer queue (if sleeping or blocked +/// on a semaphore with timeout), and deleted from the semaphore mutual +/// exclusion if blocked on a semaphore. The thread control block is then +/// marked as deleted. +/// +/// Once a thread has completed or been deleted the thread structure and +/// thread stack areas can be used for other purposes. +/// +/// \param thread The thread to delete +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors. If a +/// thread deletes itself this API does not return at all. +/// +/// \retval 0 Successful completion, including calls on a \a thread that has +/// completed or had already been deleted. +/// +/// \retval -SSX_ILLEGAL_CONTEXT_THREAD The API was called from a critical +/// interrupt context. +/// +/// \retval -SSX_INVALID_THREAD_AT_DELETE The \a thread is a null (0) pointer. + +int +ssx_thread_delete(SsxThread *thread) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF(thread == 0, SSX_INVALID_THREAD_AT_DELETE); + } + + __ssx_thread_delete(thread, SSX_THREAD_STATE_DELETED); + + return SSX_OK; +} + + +/// Complete a thread +/// +/// If a thread ever returns from the subroutine defining the thread entry +/// point, the thread is removed from all SSX kernel data structures and +/// marked completed. The thread routine can also use the API ssx_complete() +/// to make this more explicit if desired. SSX makes no distinction between +/// completed and deleted threads, but provides these indications for +/// the benefit of the application. +/// +/// Note that this API is only available from the current thread to mark its +/// own completion. +/// +/// Once a thread has completed or been deleted the thread structure and +/// thread stack areas can be used for other purposes. +/// +/// Any return value indicates an error; see \ref ssx_errors. In the event of +/// a successful completion this API does not return to the caller, which is +/// always the thread context being completed. +/// +/// \retval -SSX_ILLEGAL_CONTEXT_THREAD The API was not called from a thread +/// context. + +// Note: Casting __ssx_current_thread removes the 'volatile' attribute. + +int +ssx_complete(void) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_UNLESS_THREAD_CONTEXT(); + } + + __ssx_thread_delete((SsxThread *)__ssx_current_thread, + SSX_THREAD_STATE_COMPLETED); + + return SSX_OK; +} + + +/// Sleep a thread until an absolute time +/// +/// \param time An absolute time as measured by the SSX timebase +/// +/// Threads can use this API to sleep until an absolute time. Sleeping threads +/// are not scheduled, although they maintain their priorities. This differs +/// from thread suspension, where the suspended thread relinquishes its +/// priority. When the sleep timer times out the thread becomes runnable +/// again, and will run as soon as it becomes the highest-priority mapped +/// runnable thread. +/// +/// Sleeping threads may also be later suspended. In this case the Sleep timer +/// continues to run, and if it times out before the thread is resumed the +/// thread will be immediately runnable when it is resumed. +/// +/// See the SSX specification for a full discussion of how SSX handles +/// scheduling events at absolute times "in the past". Briefly stated, if the +/// \a time is in the past, the thread will Sleep for the briefest possible +/// period supported by the hardware. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion. +/// +/// \retval -SSX_ILLEGAL_CONTEXT_THREAD The API was not called from a thread +/// context. + +// Note: Casting __ssx_current_thread removes the 'volatile' attribute. + +int +ssx_sleep_absolute(SsxTimebase time) +{ + SsxMachineContext ctx; + SsxThread *current; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_UNLESS_THREAD_CONTEXT(); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + current = (SsxThread *)__ssx_current_thread; + + current->timer.timeout = time; + __ssx_timer_schedule(&(current->timer)); + + current->flags |= SSX_THREAD_FLAG_TIMER_PEND; + + SSX_TRACE_THREAD_SLEEP(current->priority); + + __ssx_thread_queue_delete(&__ssx_run_queue, current->priority); + __ssx_schedule(); + + current->flags &= ~(SSX_THREAD_FLAG_TIMER_PEND | SSX_THREAD_FLAG_TIMED_OUT); + + ssx_critical_section_exit(&ctx); + + return SSX_OK; +} + +/// Sleep a thread for an interval relative to the current time. +/// +/// \param interval A time interval relative to the current timebase. +/// +/// Threads can use this API to sleep for a time relative to the current +/// timebase. The absolute timeout is \c ssx_timebase_get() + \a interval. +/// +/// Sleeping threads are not scheduled, although they maintain their +/// priorities. This differs from thread suspension, where the suspended +/// thread relinquishes its priority. When the sleep timer times out the +/// thread becomes runnable again, and will run as soon as it becomes the +/// highest-priority mapped runnable thread. +/// +/// Sleeping threads may also be later suspended. In this case the Sleep timer +/// continues to run, and if it times out before the thread is resumed the +/// thread will be immediately runnable when it is resumed. +/// +/// See the SSX specification for a full discussion of how SSX handles +/// scheduling events at absolute times "in the past". Briefly stated, if the +/// \a interval is 0 or is so small that the absolute time becomes a "past" +/// time before the Sleep is actually scheduled, the thread will Sleep for the +/// briefest possible period supported by the hardware. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion. +/// +/// \retval -SSX_ILLEGAL_CONTEXT_THREAD The API was not called from a thread +/// context. + +int +ssx_sleep(SsxInterval interval) +{ + return ssx_sleep_absolute(ssx_timebase_get() + interval); +} + + +/// Get information about a thread. +/// +/// \param thread A pointer to the SsxThread to query +/// +/// \param state The value returned through this pointer is the current state +/// of the thread; See \ref ssx_thread_states. The caller can set this +/// parameter to the null pointer (0) if this information is not required. +/// +/// \param priority The value returned through this pointer is the current +/// priority of the thread. The caller can set this parameter to the null +/// pointer (0) if this information is not required. +/// +/// \param runnable The value returned through this pointer is 1 if the thread +/// is in state SSX_THREAD_STATE_MAPPED and is currently in the run queue +/// (i.e., neither blocked on a semaphore nor sleeping), otherwise 0. The +/// caller can set this parameter to the null pointer (0) if this information +/// is not required. +/// +/// The information returned by this API can only be guaranteed consistent if +/// the API is called from an SSX_NONCRITICAL critical section. Since the +/// implementation of this API does not enforce a critical section, it is not +/// an error to call this API from a critical interrupt context. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_INVALID_THREAD_AT_INFO The \a thread is a null (0) pointer. + +int +ssx_thread_info_get(SsxThread *thread, + SsxThreadState *state, + SsxThreadPriority *priority, + int *runnable) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(thread == 0, SSX_INVALID_THREAD_AT_INFO); + } + + if (state) { + *state = thread->state; + } + if (priority) { + *priority = thread->priority; + } + if (runnable) { + *runnable = ((thread->state == SSX_THREAD_STATE_MAPPED) && + __ssx_thread_queue_member(&__ssx_run_queue, + thread->priority)); + } + return SSX_OK; +} + + +/// Change the priority of a thread. +/// +/// \param thread The thread whose priority will be changed +/// +/// \param new_priority The new priority of the thread +/// +/// \param old_priority The value returned through this pointer is the +/// old priority of the thread prior to the change. The caller can set +/// this parameter to the null pointer (0) if this information is not +/// required. +/// +/// Thread priorities can be changed by the \c ssx_thread_priority_change() +/// API. This call will fail if the thread pointer is invalid or if the thread +/// is mapped and the new priority is currently in use. The call will succeed +/// even if the \a thread is suspended, completed or deleted. The +/// application-level scheduling algorithm is completely responsible for the +/// correctness of the application in the event of suspended, completed or +/// deleted threads. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion, including the redundant case of +/// attempting to change the priority of the thread to its current priority. +/// +/// \retval -SSX_ILLEGAL_CONTEXT_THREAD the API was called from a critical +/// interrupt context. +/// +/// \retval -SSX_INVALID_THREAD_AT_CHANGE The \a thread is null (0) or +/// otherwise invalid. +/// +/// \retval -SSX_INVALID_ARGUMENT_THREAD_CHANGE The \a new_priority is invalid. +/// +/// \retval -SSX_PRIORITY_IN_USE_AT_CHANGE The \a thread is mapped and the \a +/// new_priority is currently in use by another thread. + +int +ssx_thread_priority_change(SsxThread *thread, + SsxThreadPriority new_priority, + SsxThreadPriority *old_priority) +{ + SsxMachineContext ctx; + SsxThreadPriority priority; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF(thread == 0, SSX_INVALID_THREAD_AT_CHANGE); + SSX_ERROR_IF(new_priority > SSX_THREADS, + SSX_INVALID_ARGUMENT_THREAD_CHANGE); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + priority = thread->priority; + + if (priority != new_priority) { + + if (!__ssx_thread_is_mapped(thread)) { + + thread->priority = new_priority; + + } else { + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL(__ssx_priority_map[new_priority] != 0, + SSX_PRIORITY_IN_USE_AT_CHANGE, + &ctx); + } + + __ssx_thread_unmap(thread); + thread->priority = new_priority; + __ssx_thread_map(thread); + __ssx_schedule(); + } + } + + if (old_priority) { + *old_priority = priority; + } + + ssx_critical_section_exit(&ctx); + + return SSX_OK; +} + + +/// Return a pointer to the thread (if any) mapped at a given priority. +/// +/// \param priority The thread priority of interest +/// +/// \param thread The value returned through this pointer is a pointer to the +/// thread currently mapped at the given priority level. If no thread is +/// mapped, or if the \a priority is the priority of the idle thread, the +/// pointer returned will be null (0). +/// +/// The information returned by this API can only be guaranteed consistent if +/// the API is called from an SSX_NONCRITICAL critical section. Since the +/// implementation of this API does not require a critical section, it is not +/// an error to call this API from a critical interrupt context. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion. +/// +/// \retval -SSX_INVALID_ARGUMENT_THREAD_PRIORITY The \a priority is invalid +/// or the \a thread parameter is null (0). + +int +ssx_thread_at_priority(SsxThreadPriority priority, + SsxThread **thread) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((priority > SSX_THREADS) || (thread == 0), + SSX_INVALID_ARGUMENT_THREAD_PRIORITY); + } + + *thread = __ssx_thread_at_priority(priority); + + return SSX_OK; +} + + +/// Swap thread priorities +/// +/// \param thread_a A pointer to an initialized SsxThread +/// +/// \param thread_b A pointer to an initialized SsxThread +/// +/// This API swaps the priorities of \a thread_a and \a thread_b. The API is +/// provided to support general and efficient application-directed scheduling +/// algorithms. The requirements on the \a thread_a and \a thread_b arguments +/// are that they are valid pointers to initialized SsxThread structures, that +/// the current thread priorities of both threads are legal, and that if a +/// thread is currently mapped, that the new thread priority is not otherwise +/// in use. +/// +/// The API does not require either thread to be mapped, or even to be active. +/// It is legal for one or both of the swap partners to be suspended, deleted +/// or completed threads. The application is completely responsible for the +/// correctness of scheduling algorithms that might operate on inactive or +/// suspended threads. +/// +/// The API does not change the mapped status of a thread. A thread will be +/// mapped after the call of ssx_thread_priority_swap() if and only if it was +/// mapped prior to the call. If the new priority of a mapped thread is +/// currently in use (by a thread other than the swap partner), then the +/// SSX_PRIORITY_IN_USE_AT_SWAP error is signalled and the swap does not take +/// place. This could only happen if the swap partner is not currently mapped. +/// +/// It is legal for a thread to swap its own priority with another thread. The +/// degenerate case that \a thread_a and \a thread_b are equal is also legal - +/// but has no effect. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion, including the redundant cases that do not +/// actually change priorities, or the cases that assign new priorities to +/// suspended, completed or deleted threads. +/// +/// \retval -SSX_ILLEGAL_CONTEXT_THREAD the API was called from a critical +/// interrupt context. +/// +/// \retval -SSX_INVALID_THREAD_AT_SWAP1 One or both of \a thread_a and +/// \a thread_b is null (0) or otherwise invalid, +/// +/// \retval -SSX_INVALID_THREAD_AT_SWAP2 the priorities of One or both of +/// \a thread_a and \a thread_b are invalid. +/// +/// \retval -SSX_INVALID_ARGUMENT One or both of the priorities +/// of \a thread_a and \a thread_b is invalid. +/// +/// \retval -SSX_PRIORITY_IN_USE_AT_SWAP Returned if a thread is mapped and the +/// new thread priority is currently in use by another thread (other than the +/// swap partner). + +int +ssx_thread_priority_swap(SsxThread* thread_a, SsxThread* thread_b) +{ + SsxMachineContext ctx; + SsxThreadPriority priority_a, priority_b; + int mapped_a, mapped_b; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF((thread_a == 0) || (thread_b == 0), + SSX_INVALID_THREAD_AT_SWAP1); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + if (thread_a != thread_b) { + + mapped_a = __ssx_thread_is_mapped(thread_a); + mapped_b = __ssx_thread_is_mapped(thread_b); + priority_a = thread_a->priority; + priority_b = thread_b->priority; + + if (SSX_ERROR_CHECK_API) { + int priority_in_use; + SSX_ERROR_IF_CRITICAL((priority_a > SSX_THREADS) || + (priority_b > SSX_THREADS), + SSX_INVALID_THREAD_AT_SWAP2, + &ctx); + priority_in_use = + (mapped_a && !mapped_b && + (__ssx_thread_at_priority(priority_b) != 0)) || + (!mapped_a && mapped_b && + (__ssx_thread_at_priority(priority_a) != 0)); + SSX_ERROR_IF_CRITICAL(priority_in_use, + SSX_PRIORITY_IN_USE_AT_SWAP, &ctx); + } + + if (mapped_a) { + __ssx_thread_unmap(thread_a); + } + if (mapped_b) { + __ssx_thread_unmap(thread_b); + } + thread_a->priority = priority_b; + thread_b->priority = priority_a; + if (mapped_a) { + __ssx_thread_map(thread_a); + } + if (mapped_b) { + __ssx_thread_map(thread_b); + } + __ssx_schedule(); + } + + ssx_critical_section_exit(&ctx); + + return SSX_OK; +} + + +#undef __SSX_THREAD_CORE_C__ diff --git a/src/ssx/ssx/ssx_thread_init.c b/src/ssx/ssx/ssx_thread_init.c new file mode 100755 index 0000000..c1a71b5 --- /dev/null +++ b/src/ssx/ssx/ssx_thread_init.c @@ -0,0 +1,140 @@ +// $Id: ssx_thread_init.c,v 1.2 2014/02/03 01:30:44 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssx_thread_init.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_thread_init.c +/// \brief SSX thread API initialization routines +/// +/// The entry points in this file are routines that are typically used during +/// initialization, and their code space could be deallocated and recovered if +/// no longer needed by the application after initialization. + +#include "ssx.h" + +/// Create (initialize) a thread +/// +/// \param thread A pointer to an SsxThread structure to initialize +/// +/// \param thread_routine The subroutine that implements the thread +/// +/// \param arg Private data to be passed as the argument to the thread +/// routine when it begins execution +/// +/// \param stack The stack space of the thread +/// +/// \param stack_size The size of the stack in bytes +/// +/// \param priority The initial priority of the thread +/// +/// The \a thread argument must be a pointer to an uninitialized or completed +/// or deleted thread. This \c SsxThread structure \em is the thread, so this +/// memory area must not be modified by the application until the thread +/// completes or is deleted. SSX can not tell if an SsxThread structure is +/// currently in use as a thread control block.ssx_thread_create() will +/// silently overwrite an SsxThread structure that is currently in use. +/// +/// The stack area must be large enough to hold the dynamic stack requirements +/// of the entry point routine, and all subroutines and functions that might +/// be invoked on any path from the entry point. The stack must also always +/// be able to hold the thread context in the event the thread is preempted, +/// plus other critical context. SSX aligns stack areas in machine-specific +/// ways, so that the actual stack area may reduced in size slightly if it is +/// not already aligned. +/// +/// Threads are created runnable but unmapped. A newly created thread will +/// not be eligible to run until a call of ssx_thread_resume() targets the +/// thread. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_INVALID_THREAD_AT_CREATE The \a thread is a null (0) pointer. +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical interrupt +/// context. +/// +/// \retval -SSX_INVALID_ARGUMENT_THREAD1 the \a thread_routine is null (0) +/// +/// \retval -SSX_INVALID_ARGUMENT_THREAD2 the \a priority is invalid, +/// +/// \retval -SSX_INVALID_ARGUMENT_THREAD3 the stack area wraps around +/// the end of memory. +/// +/// \retval -SSX_STACK_OVERFLOW The stack area at thread creation is smaller +/// than the minimum safe size. + +int +ssx_thread_create(SsxThread *thread, + SsxThreadRoutine thread_routine, + void *arg, + SsxAddress stack, + size_t stack_size, + SsxThreadPriority priority) +{ + int rc; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF(thread == 0, SSX_INVALID_THREAD_AT_CREATE); + SSX_ERROR_IF((thread_routine == 0) || + (priority >= SSX_THREADS), + SSX_INVALID_ARGUMENT_THREAD1); + } + + rc = __ssx_stack_init(&stack, &stack_size); + if (rc) { + return rc; + } + + thread->saved_stack_pointer = stack; + thread->stack_base = stack; + + if (SSX_STACK_DIRECTION < 0) { + + thread->stack_limit = stack - stack_size; + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(thread->stack_limit > thread->stack_base, + SSX_INVALID_ARGUMENT_THREAD2); + } + + } else { + + thread->stack_limit = stack + stack_size; + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(thread->stack_limit < thread->stack_base, + SSX_INVALID_ARGUMENT_THREAD3); + } + } + + thread->semaphore = 0; + thread->priority = priority; + thread->state = SSX_THREAD_STATE_SUSPENDED_RUNNABLE; + thread->flags = 0; + + ssx_timer_create_nonpreemptible(&(thread->timer), + __ssx_thread_timeout, + (void *)thread); + + __ssx_thread_context_initialize(thread, thread_routine, arg); + + return rc; +} + + + + + + + + + + + + + + diff --git a/src/ssx/ssx/ssx_timer_core.c b/src/ssx/ssx/ssx_timer_core.c new file mode 100755 index 0000000..8153acf --- /dev/null +++ b/src/ssx/ssx/ssx_timer_core.c @@ -0,0 +1,447 @@ +// $Id: ssx_timer_core.c,v 1.2 2014/02/03 01:30:44 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssx_timer_core.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_timer_core.c +/// \brief SSX portable kernel timer handler +/// +/// This file contains core routines that would be needed by any application +/// that requires SSX timer support at runtime. +/// +/// SSX implements a 'tickless' kernel - all events are scheduled at absolute +/// times of the SSX timebase. This approach gives the application full +/// control over granularity of event scheduling. Scheduling in absolute time +/// opens up the possibility of scheduling events "in the past". SSX +/// uniformly handles this case by scheduling "past" events to occur 1 +/// timebase tick in the future, so that timer callbacks are always run in the +/// expected noncritical interrupt context. +/// +/// SSX implements the time queue as a simple unordered list of events, plus a +/// dedicated variable that holds the earliest timeout of any event in the +/// list. This is thought to be an appropriate data structure for the +/// following reasons: +/// +/// - SSX applications will be small and will not schedule a large number of +/// events. Therefore the cost of scanning the list each time an event times +/// out is balanced against the cost of maintaining the list as a sorted data +/// structure each time an event is added or removed from the event queue. +/// +/// - SSX applications may schedule and cancel many, many more events (safety +/// timeouts) than are ever allowed to expire. Events can be added and deleted +/// from the simple DEQUE very quickly since there is no sorting +/// overhead. +/// +/// Events are added to the queue simply by placing them at the end of the +/// queue. If the new event times out earlier than the previous earliest +/// event, the hardware timeout is rescheduled for the new event time. Events +/// are deleted from the queue (cancelled) simply by deleting them. Deletion +/// does not affect the hardware timeout, even if the deleted event would have +/// been the next to time out. It is not an error for the timer handler to +/// take a timer interrupt and find no events pending. Pending events can +/// also be rescheduled in place. +/// +/// When a timeout occurs the event list is scanned from the beginning, and +/// any event that has timed out is rescheduled if necessary (periodic events) +/// and its callback is processed. Since event and callback processing take +/// time, the list is potentially scanned multiple times until there are no +/// more timed-out events in the list. +/// +/// Note that callbacks are not necessarily processed in time-order. In this +/// sense the SSX time queue is like a traditional tick-based time queue in +/// that events are effectively lumped into groups of events that time out +/// together. In a tick-based kernel the 'lump' is the tick interval; here +/// the 'lump' is a variable interval that corresponds to the time it takes to +/// process the entire event list. +/// +/// Timer callbacks are typically run with interrupt preemption enabled. +/// Special callbacks may run without preemption. This is the only part of +/// the SSX kernel where data structures of indeterminate size are processed. +/// During processing of the event list by the timer interrupt handler, the +/// consideration of each event always includes a window of preemptability. + +#define __SSX_TIMER_CORE_C__ + +#include "ssx.h" + +// This routine is only used in this file, and will always be called in +// critical section. + +static inline int +timer_active(SsxTimer* timer) +{ + return ssx_deque_is_queued((SsxDeque*)timer); +} + + +// This is the kernel version of ssx_timer_cancel(). +// +// This routine is used here and by thread and semaphore routines. +// Noncritical interrupts must be disabled at entry. +// +// If the timer is active, then there is a special case if we are going to +// delete the 'cursor' - that is the timer that __ssx_timer_handler() is going +// to handle next. In this case we need to move the cursor to the next timer +// in the queue. +// +// Note that cancelling a timer does not cause a re-evaluation of the next +// timeout. This will happen naturally when the current timeout expires. + +int +__ssx_timer_cancel(SsxTimer *timer) +{ + int rc; + SsxDeque* timer_deque = (SsxDeque*)timer; + SsxTimeQueue* tq = &__ssx_time_queue; + + if (!timer_active(timer)) { + + rc = -SSX_TIMER_NOT_ACTIVE; + + } else { + + if (timer_deque == tq->cursor) { + tq->cursor = tq->cursor->next; + } + ssx_deque_delete(timer_deque); + rc = 0; + } + return rc; +} + + +// This is the kernel version of ssx_timer_schedule(). +// +// This routine is used here and by thread and semaphore routines. +// Noncritical interrupts must be disabled at entry. +// +// Unless the timer is already active it is enqueued in the doubly-linked +// timer list by inserting the timer at the end of the queue. Then the +// hardware timeout is scheduled if necessary. If the time queue 'cursor' != 0 +// we are in the midst of processing the time queue, and the end of time queue +// processing will schedule the next hardware timemout. + +void +__ssx_timer_schedule(SsxTimer* timer) +{ + SsxTimeQueue* tq = &__ssx_time_queue; + + if (!timer_active(timer)) { + ssx_deque_push_back((SsxDeque*)tq, (SsxDeque*)timer); + } + + if (timer->timeout < tq->next_timeout) { + tq->next_timeout = timer->timeout; + if (tq->cursor == 0) { + __ssx_schedule_hardware_timeout(tq->next_timeout); + } + } +} + + +// The tickless timer mechanism has timed out. Note that due to timer +// deletions and other factors, there may not actually be a timer in the queue +// that has timed out - but it doesn't matter (other than for efficiency). +// +// Noncritical interrupts are (must be) disabled at entry, and this invariant +// is checked. This routine must not be entered reentrantly. +// +// First, time out any timers that have expired. Timers in the queue are +// unordered, so we have to check every one. Since passing through the +// loop takes time, we may have to make multiple passes until we know +// that there are no timers in the queue that have already timed +// out. Note that it would also work to only go through the loop once and +// let the hardware scheduler take care of looping, but that would imply +// more overhead than the current implementation. +// +// On each pass through the loop tq->next_timeout computes the minimum timeout +// of events remaining in the queue. This is the only part of the kernel that +// searches a list of indefinite length. Kernel interrupt latency is mitigated +// by running callbacks with interrupts disabled either during or after the +// call for timed out events, and also after every check for events that have +// not timed out. +// +// Because interrupt preemption is enabled during processing, and preempting +// handlers may invoke time queue operations, we need to establish a pointer +// to the next entry to be examined (tq->cursor) before enabling interupts. +// It's possible that this pointer will be changed by other interrupt handlers +// that cancel the timer pointed to by tq->cursor. +// +// The main loop iterates on the SsxDeque form of the time queue, casting each +// element back up to the SsxTimer as it is processed. + +void +__ssx_timer_handler() +{ + SsxTimeQueue* tq; + SsxTimebase now; + SsxTimer* timer; + SsxDeque* timer_deque; + SsxTimerCallback callback; + + tq = &__ssx_time_queue; + + if (SSX_ERROR_CHECK_KERNEL) { + if (tq->cursor != 0) { + SSX_PANIC(SSX_TIMER_HANDLER_INVARIANT); + } + } + + while ((now = ssx_timebase_get()) >= tq->next_timeout) { + + tq->next_timeout = SSX_TIMEBASE_MAX; + timer_deque = ((SsxDeque*)tq)->next; + + while (timer_deque != (SsxDeque*)tq) { + + timer = (SsxTimer*)timer_deque; + tq->cursor = timer_deque->next; + + if (timer->timeout <= now) { + + // The timer timed out. It is removed from the queue unless + // it is a peridic timer that needs to be rescheduled. We do + // rescheduling here in the critical section to correctly + // handle timers whose callbacks may cancel the timer. The + // timer is rescheduled in absolute time. + // + // The callback may be made with interrupt preemption enabled + // or disabled. However to mitigate kernel interrupt latency + // we go ahead and open up to interrupts after the callback if + // the callback itself was not preemptible. + + if (timer->period == 0) { + ssx_deque_delete(timer_deque); + } else { + timer->timeout += timer->period; + tq->next_timeout = MIN(timer->timeout, tq->next_timeout); + } + + callback = timer->callback; + if (callback) { + if (timer->options & SSX_TIMER_CALLBACK_PREEMPTIBLE) { + ssx_interrupt_preemption_enable(); + callback(timer->arg); + } else { + callback(timer->arg); + ssx_interrupt_preemption_enable(); + } + } + ssx_interrupt_preemption_disable(); + + } else { + + // This timer has not timed out. Its timeout will simply + // participate in the computation of the next timeout. For + // interrupt latency reasons we always allow a period of + // interrupt preemption. + + tq->next_timeout = MIN(timer->timeout, tq->next_timeout); + ssx_interrupt_preemption_enable(); + ssx_interrupt_preemption_disable(); + } + + timer_deque = tq->cursor; + } + } + + tq->cursor = 0; + + // Finally, reschedule the next timeout + + __ssx_schedule_hardware_timeout(tq->next_timeout); +} + + +/// Schedule a timer in absolute time. +/// +/// \param timer The SsxTimer to schedule. +/// +/// \param timeout The timer will be scheduled to time out at this absolute +/// time. Note that if the \a timeout is less than the current time then the +/// timer will be scheduled at a minimum timeout in the future and the +/// callback will be executed in an interrupt context. +/// +/// \param period If non-0, then when the timer times out it will rescheduled +/// to time out again at the absolute time equal to the last timeout time plus +/// the \a period. By convention a \a period of 0 indicates a one-shot +/// timer that is not rescheduled. +/// +/// Once created with ssx_timer_create() a timer can be \e scheduled, which +/// queues the timer in the kernel time queue. It is not an error to call +/// ssx_timer_schedule() on a timer that is already scheduled in the time +/// queue - the timer is simply rescheduled with the new characteristics. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_INVALID_TIMER_AT_SCHEDULE A a null (0) pointer was provided as +/// the \a timer argument. +/// +/// \retval -SSX_ILLEGAL_CONTEXT_TIMER The call was made from a critical +/// interrupt context. + +int +ssx_timer_schedule_absolute(SsxTimer *timer, + SsxTimebase timeout, + SsxInterval period) + +{ + SsxMachineContext ctx; + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(timer == 0, SSX_INVALID_TIMER_AT_SCHEDULE); + SSX_ERROR_IF(__ssx_kernel_context_critical_interrupt(), + SSX_ILLEGAL_CONTEXT_TIMER); + } + + timer->timeout = timeout; + timer->period = period; + __ssx_timer_schedule(timer); + + ssx_critical_section_exit(&ctx); + + return SSX_OK; +} + + +/// Schedule a timer for an interval relative to the current time. +/// +/// \param timer The SsxTimer to schedule. +/// +/// \param interval The timer will be scheduled to time out at the current +/// time (ssx_timebase_get()) plus this \a interval. +/// +/// \param period If non-0, then when the timer times out it will rescheduled +/// to time out again at the absolute time equal to the last timeout time plus +/// the \a period. By convention a \a period of 0 indicates a one-shot +/// timer that is not rescheduled. +/// +/// Once created with ssx_timer_create() a timer can be \e scheduled, which +/// queues the timer in the kernel time queue. It is not an error to call \c +/// ssx_timer_schedule() on a timer that is already scheduled in the time +/// queue - the timer is simply rescheduled with the new characteristics. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_INVALID_TIMER_AT_SCHEDULE A a null (0) pointer was provided as +/// the \a timer argument. +/// +/// \retval -SSX_ILLEGAL_CONTEXT_TIMER The call was made from a critical +/// interrupt context. + +int +ssx_timer_schedule(SsxTimer *timer, + SsxInterval interval, + SsxInterval period) +{ + return ssx_timer_schedule_absolute(timer, + ssx_timebase_get() + interval, + period); +} + + +/// Cancel (dequeue) a timer. +/// +/// \param timer The SsxTimer to cancel. +/// +/// Timers can be canceled at any time. It is never an error to call +/// ssx_timer_cancel() on an SsxTimer object after it is created. Memory used +/// by an SsxTimer can be safely reused for another purpose after a successful +/// call ofssx_timer_cancel(). +/// +/// Return values other than SSX_OK (0) are not necessarily errors; see \ref +/// ssx_errors +/// +/// The following return codes are non-error codes: +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_TIMER_NOT_ACTIVE The \a timer is not currently scheduled, +/// i.e. it was never scheduled or has timed out. This code is returned for +/// information only and is not considered an error. +/// +/// The following return codes are error codes: +/// +/// \retval -SSX_INVALID_TIMER_AT_CANCEL The \a timer is a null (0) pointer. +/// +/// \retval -SSX_ILLEGAL_CONTEXT_TIMER The call was made from a critical +/// interrupt context. +/// + +int +ssx_timer_cancel(SsxTimer *timer) +{ + SsxMachineContext ctx; + int rc = SSX_OK; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF(timer == 0, SSX_INVALID_TIMER_AT_CANCEL); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + rc = __ssx_timer_cancel(timer); + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +/// Get information about a timer. +/// +/// \param timer The SsxTimer to query +/// +/// \param timeout The API returns the absolute timeout of the timer through +/// this pointer. If the timer is active, this is the current timeout. If +/// the timer has timed out then this is the previous absolute timeout. If +/// the timer was never scheduled this will be 0. The caller can set this +/// parameter to the null pointer (0) if this information is not required. +/// +/// \param active If the value returned through this pointer is 1 then the +/// timer is active (currently scheduled), otherwise the value will be 0 +/// indicating an inactive timer. The caller can set this parameter to the +/// null pointer (0) if this information is not required. +/// +/// The information returned by this API can only be guaranteed consistent if +/// the API is called from an SSX_NONCRITICAL critical section. Since the +/// implementation of this API does not require a critical section, it is not +/// an error to call this API from a critical interrupt context. +/// +/// Return values other than SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_INVALID_TIMER_AT_INFO The \a timer is a null (0) pointer. + +int +ssx_timer_info_get(SsxTimer *timer, + SsxTimebase *timeout, + int *active) + +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(timer == 0, SSX_INVALID_TIMER_AT_INFO); + } + + if (timeout) { + *timeout = timer->timeout; + } + if (active) { + *active = timer_active(timer); + } + + return SSX_OK; +} + +#undef __SSX_TIMER_CORE_C__ diff --git a/src/ssx/ssx/ssx_timer_init.c b/src/ssx/ssx/ssx_timer_init.c new file mode 100755 index 0000000..6c35ea6 --- /dev/null +++ b/src/ssx/ssx/ssx_timer_init.c @@ -0,0 +1,124 @@ +// $Id: ssx_timer_init.c,v 1.2 2014/02/03 01:30:44 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssx_timer_init.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_timer_init.c +/// \brief SSX timer initialization +/// +/// The entry points in this file might only be used during initialization of +/// the application. In this case the code space for these routines could be +/// recovered and reused after initialization. + +#include "ssx.h" + +// Implementation of timer creation + +static int +_ssx_timer_create(SsxTimer *timer, + SsxTimerCallback callback, + void *arg, + int options) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((timer == 0), SSX_INVALID_TIMER_AT_CREATE); + } + + ssx_deque_element_create((SsxDeque*)timer); + timer->timeout = 0; + timer->period = 0; + timer->callback = callback; + timer->arg = arg; + timer->options = options; + + return SSX_OK; +} + + +/// Create (initialize) a preemptible timer. +/// +/// \param timer The SsxTimer to initialize. +/// +/// \param callback The timer callback +/// +/// \param arg Private data provided to the callback. +/// +/// Once created with ssx_timer_create() a timer can be scheduled with +/// ssx_timer_schedule() or ssx_timer_schedule_absolute(), which queues the +/// timer in the kernel time queue. Timers can be cancelled by a call of +/// ssx_timer_cancel(). +/// +/// Timers created with ssx_timer_create() are always run as noncritical +/// interrupt handlers with interrupt preemption enabled. Timer callbacks are +/// free to enter critical sections of any priorioty if required, but must +/// always exit with noncritical interrupts enabled. +/// +/// Caution: SSX has no way to know if an SsxTimer structure provided to +/// ssx_timer_create() is safe to use as a timer, and will silently modify +/// whatever memory is provided. +/// +/// Return values other then SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_INVALID_TIMER_AT_CREATE The \a timer is a null (0) pointer. + +int +ssx_timer_create(SsxTimer *timer, + SsxTimerCallback callback, + void *arg) +{ + return _ssx_timer_create(timer, callback, arg, + SSX_TIMER_CALLBACK_PREEMPTIBLE); +} + + +/// Create (initialize) a nonpreemptible timer. +/// +/// \param timer The SsxTimer to initialize. +/// +/// \param callback The timer callback +/// +/// \param arg Private data provided to the callback. +/// +/// Once created with ssx_timer_create_preemptible() a timer can be scheduled +/// with ssx_timer_schedule() or ssx_timer_schedule_absolute(), which queues +/// the timer in the kernel time queue. Timers can be cancelled by a call of +/// ssx_timer_cancel(). +/// +/// Timers created with ssx_timer_create_nonpreemptible() are always run as +/// noncritical interrupt handlers with interrupt preemption disabled. Timer +/// callbacks are free to later enable preemption if desired, but must always +/// exit with noncritical interrupts disabled. +/// +/// \note The use of ssx_timer_create_nonpreemptible() should be rare, and the +/// timer callbacks should be short and sweet to avoid long interrupt +/// latencies for other interrupts. This API was initially introduced for use +/// by the SSX kernel itself when scheduling thread-timer callbacks to avoid +/// potential race conditions with other interrupts that may modify thread +/// state or the state of the time queue. Applications may also require this +/// facility to guarantee a consistent state in the event that other +/// interrupts may cancel the timer. +/// +/// Caution: SSX has no way to know if an SsxTimer structure provided to +/// ssx_timer_create() is safe to use as a timer, and will silently modify +/// whatever memory is provided. +/// +/// Return values other then SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_INVALID_TIMER_AT_CREATE The \a timer is a null (0) pointer. + +int +ssx_timer_create_nonpreemptible(SsxTimer *timer, + SsxTimerCallback callback, + void *arg) +{ + return _ssx_timer_create(timer, callback, arg, 0); +} + + diff --git a/src/ssx/ssx/ssxssxfiles.mk b/src/ssx/ssx/ssxssxfiles.mk new file mode 100755 index 0000000..e78e986 --- /dev/null +++ b/src/ssx/ssx/ssxssxfiles.mk @@ -0,0 +1,35 @@ +# $Id: ssxssxfiles.mk,v 1.2 2014/06/26 13:02:00 cmolsen Exp $ +# $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/ssx/ssx/ssxssxfiles.mk,v $ +# @file ssxpgpfiles.mk +# +# @brief mk for including pgp object files +# +# @page ChangeLogs Change Logs +# @section ssxpgpfiles.mk +# @verbatim +# +# +# Change Log ****************************************************************** +# Flag Defect/Feature User Date Description +# ------ -------------- ---------- ------------ ----------- +# @pb00E pbavari 03/11/2012 Makefile ODE support +# +# @endverbatim +# +########################################################################## +# Include +########################################################################## + + +########################################################################## +# Object Files +########################################################################## +SSX-C-SOURCES = ssx_core.c ssx_init.c ssx_stack_init.c + +SSX-TIMER-C-SOURCES += ssx_timer_core.c ssx_timer_init.c + +SSX-THREAD-C-SOURCES += ssx_thread_init.c ssx_thread_core.c \ + ssx_semaphore_init.c ssx_semaphore_core.c + +SSX_OBJECTS += $(SSX-C-SOURCES:.c=.o) + |