#ifndef __PK_API_H__ #define __PK_API_H__ //----------------------------------------------------------------------------- // *! (C) Copyright International Business Machines Corp. 2014 // *! All Rights Reserved -- Property of IBM // *! *** IBM Confidential *** //----------------------------------------------------------------------------- /// \file pk_api.h /// \brief Macros and declarations for the PK API. // Basic constants /// Although the number of threads is defined as a manifest constant, /// numerous parts of the PK code assume this definition. The number of /// supported threads _can not_ be changed simply by changing this constant. #define PK_THREADS 32 #define PK_IDLE_THREAD_PRIORITY PK_THREADS // Interrupt API #define PK_IRQ_POLARITY_ACTIVE_LOW 0 #define PK_IRQ_POLARITY_ACTIVE_HIGH 1 #define PK_IRQ_TRIGGER_LEVEL_SENSITIVE 0 #define PK_IRQ_TRIGGER_EDGE_SENSITIVE 1 // API return codes #define PK_OK 0 #define PK_ILLEGAL_CONTEXT_THREAD_CONTEXT 0x00779002 #define PK_ILLEGAL_CONTEXT_INTERRUPT_CONTEXT 0x00779003 #define PK_ILLEGAL_CONTEXT_THREAD 0x00779004 #define PK_ILLEGAL_CONTEXT_TIMER 0x00779005 #define PK_INVALID_THREAD_AT_RESUME1 0x00779007 #define PK_INVALID_THREAD_AT_RESUME2 0x00779008 #define PK_INVALID_THREAD_AT_SUSPEND1 0x00779009 #define PK_INVALID_THREAD_AT_SUSPEND2 0x0077900a #define PK_INVALID_THREAD_AT_DELETE 0x0077900b #define PK_INVALID_THREAD_AT_INFO 0x0077900c #define PK_INVALID_THREAD_AT_CHANGE 0x0077900d #define PK_INVALID_THREAD_AT_SWAP1 0x0077900e #define PK_INVALID_THREAD_AT_SWAP2 0x0077900f #define PK_INVALID_THREAD_AT_CREATE 0x00779010 #define PK_INVALID_SEMAPHORE_AT_POST 0x00779011 #define PK_INVALID_SEMAPHORE_AT_PEND 0x00779012 #define PK_INVALID_SEMAPHORE_AT_RELEASE 0x00779013 #define PK_INVALID_SEMAPHORE_AT_INFO 0x00779014 #define PK_INVALID_SEMAPHORE_AT_CREATE 0x00779015 #define PK_INVALID_TIMER_AT_SCHEDULE 0x00779016 #define PK_INVALID_TIMER_AT_CANCEL 0x00779017 #define PK_INVALID_TIMER_AT_INFO 0x00779018 #define PK_INVALID_TIMER_AT_CREATE 0x00779019 #define PK_INVALID_ARGUMENT_IRQ_SETUP 0x0077901a #define PK_INVALID_ARGUMENT_IRQ_HANDLER 0x0077901b #define PK_INVALID_ARGUMENT_INTERRUPT 0x00779024 #define PK_INVALID_ARGUMENT_CONTEXT_SET 0x00779025 #define PK_INVALID_ARGUMENT_CONTEXT_GET 0x00779026 #define PK_INVALID_ARGUMENT_FIT 0x00779027 #define PK_INVALID_ARGUMENT_WATCHDOG 0x00779028 #define PK_INVALID_ARGUMENT_INIT 0x00779029 #define PK_INVALID_ARGUMENT_SEMAPHORE 0x0077902a #define PK_INVALID_ARGUMENT_THREAD_CHANGE 0x0077902b #define PK_INVALID_ARGUMENT_THREAD_PRIORITY 0x0077902c #define PK_INVALID_ARGUMENT_THREAD1 0x0077902d #define PK_INVALID_ARGUMENT_THREAD2 0x0077902e #define PK_INVALID_ARGUMENT_THREAD3 0x0077902f #define PK_STACK_OVERFLOW 0x00779030 #define PK_TIMER_ACTIVE 0x00779031 #define PK_TIMER_NOT_ACTIVE 0x00779032 #define PK_PRIORITY_IN_USE_AT_RESUME 0x00779033 #define PK_PRIORITY_IN_USE_AT_CHANGE 0x00779034 #define PK_PRIORITY_IN_USE_AT_SWAP 0x00779035 #define PK_SEMAPHORE_OVERFLOW 0x00779036 #define PK_SEMAPHORE_PEND_NO_WAIT 0x00779037 #define PK_SEMAPHORE_PEND_TIMED_OUT 0x00779038 #define PK_SEMAPHORE_PEND_WOULD_BLOCK 0x00779039 #define PK_INVALID_DEQUE_SENTINEL 0x0077903a #define PK_INVALID_DEQUE_ELEMENT 0x0077903b #define PK_INVALID_OBJECT 0x0077903c // Kernel panics #define PK_NO_TIMER_SUPPORT 0x0077903d #define PK_START_THREADS_RETURNED 0x0077903e #define PK_UNIMPLEMENTED 0x0077903f #define PK_SCHEDULING_INVARIANT 0x00779040 #define PK_TIMER_HANDLER_INVARIANT 0x00779041 #define PK_THREAD_TIMEOUT_STATE 0x00779045 // Application-level panic offsets // (Use these as offsets for your application code panics and keep // track of them locally in your application code domain, including // sharing the panic defines with other developers making codes // for the same engine.) #define PK_APP_OFFSET_SBE 0x0077a000 #define PK_APP_OFFSET_GPE0 0x0077b000 #define PK_APP_OFFSET_GPE1 0x0077c000 #define PK_APP_OFFSET_GPE2 0x0077d000 #define PK_APP_OFFSET_GPE3 0x0077e000 #define PK_APP_OFFSET_CME 0x0077f000 /// \defgroup pk_thread_states PK Thread States /// /// Threads are created in the state PK_THREAD_STATE_SUSPENDED_RUNNABLE. /// When the thread is mapped it transitions to state PK_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 PK_THREAD_STATE_SUSPENDED_BLOCKED. For /// all threads the reason for blockage is detailed in the \a flags field of /// the thread; See \ref pk_thread_flags. PK_THREAD_STATE_DELETED and /// PK_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 PK semaphore as a thread barrier, as it supports a non-iterative /// implementation of pk_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 PK_THREAD_STATE_SUSPENDED_RUNNABLE 1 #define PK_THREAD_STATE_MAPPED 2 #define PK_THREAD_STATE_SUSPENDED_BLOCKED 3 #define PK_THREAD_STATE_COMPLETED 4 #define PK_THREAD_STATE_DELETED 5 /// @} /// \defgroup pk_thread_flags PK Thread Flags /// /// The \a flag field of the thread extends the information contained in the /// \a state field; See \ref pk_thread_states. Blocked threads will show /// PK_THREAD_FLAG_SEMAPHORE_PEND, PK_THREAD_FLAG_TIMER_PEND or both (if /// blocked on a semaphore with timeout). The flag PK_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 PK_THREAD_FLAG_SEMAPHORE_PEND and/or PK_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 /// pk_semaphore_release_all() as explained in \ref pk_thread_states. /// /// @{ #define PK_THREAD_FLAG_SEMAPHORE_PEND 0x1 #define PK_THREAD_FLAG_TIMER_PEND 0x2 #define PK_THREAD_FLAG_TIMED_OUT 0x4 /// @} // Critical Sections /// Enter a critical section, saving the current machine /// context. #define pk_critical_section_enter(pctx) \ pk_interrupt_disable(pctx) /// Exit a critical section by restoring the previous machine context. #define pk_critical_section_exit(pctx) \ pk_machine_context_set(pctx) /// Execute a statement atomically #define PK_ATOMIC(stmt) \ do { \ PkMachineContext __ctx; \ pk_critical_section_enter(&__ctx); \ stmt; \ pk_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 PK_ERROR_CHECK_API #define PK_ERROR_CHECK_API 1 #endif /// Control whether API errors cause kernel panics or return negative error /// codes. /// /// This selection is only valid if \c PK_ERROR_CHECK_API is defined /// non-0. This definition can be overriden by the application. #ifndef PK_ERROR_PANIC #define PK_ERROR_PANIC 1 #endif /// Control whether or not the PK kernel checks key invariants. /// /// Violations of kernel invariants always cause kernel panics. This /// definition can be overriden by the application. #ifndef PK_ERROR_CHECK_KERNEL #define PK_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 PkTimebase. This definition can be /// overridden by the application. #ifndef PK_TIME_INTERVAL_TYPE #define PK_TIME_INTERVAL_TYPE uint32_t #endif /// Provide support for the PkTimer APIs in addition to the default /// initerrupt APIs. This definition can be overridden by the application. #ifndef PK_TIMER_SUPPORT #define PK_TIMER_SUPPORT 1 #endif /// Provide support for the all PK APIs. Thread support requires/implies /// support for time services and semaphores. This definition can be /// overridden by the application. #ifndef PK_THREAD_SUPPORT #define PK_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 pk_initialize(). Thread stacks are prepatterned during /// \c pk_thread_create(). /// /// 2 : (\b Default - Currently Unimplemented) In addition to prepatterning, /// stack utilization is computed at the exit of context switches and /// 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 PK_STACK_CHECK #define PK_STACK_CHECK 1 #endif /// A hook for main() /// /// This hook macro is expanded in the body of __pk_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 /// pk_app_cfg.h. The PK_MAIN_HOOK will run on the stack of main(). #ifndef PK_MAIN_HOOK #define PK_MAIN_HOOK do {} while (0) #endif /// A hook for pk_start_threads() /// /// This hook macro is expanded in the call-tree of pk_start_threads() before /// threads are actually started. The application can redefine this hook /// macro in (or in headers referred to in) the application header /// pk_app_cfg.h. /// /// The PK_START_THREADS_HOOK runs as a pseudo-interrupt handler on the /// kernel stack, with external interrupts disabled. #ifndef PK_START_THREADS_HOOK #define PK_START_THREADS_HOOK do {} while (0) #endif /// The maximum value of the \c PkTimebase type. #define PK_TIMEBASE_MAX ((PkTimebase)-1) /// A special value that specifies that the timebase will not be reset during /// pk_init(). #define PK_TIMEBASE_CONTINUES PK_TIMEBASE_MAX /// By convention, a timeout value indicating 'no waiting' in a call of \c /// pk_semaphore_pend(). #define PK_NO_WAIT 0 /// By convention, a timeout value indicating 'wait forever' in a call of \c /// pk_semaphore_pend(). #define PK_WAIT_FOREVER ((PkInterval)-1) /// The PK timebase frequency in Hz /// /// Earlier version of PK defined the timbase frequency as a preprocessor /// macro. Now, the timebase frequency is specified as a parameter of the /// pk_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 PK_TIMEBASE_FREQUENCY_HZ __pk_timebase_frequency_hz /// This is the unscaled timebase frequency in Hz. #ifdef APPCFG_USE_EXT_TIMEBASE #define PK_BASE_FREQ_HZ (uint32_t)25000000 #else #define PK_BASE_FREQ_HZ (uint32_t)400000000 #endif /* APPCFG_USE_EXT_TIMEBASE */ #define PK_BASE_FREQ_KHZ (PK_BASE_FREQ_HZ / 1000) #define PK_BASE_FREQ_MHZ (PK_BASE_FREQ_HZ / 1000000) /// Scale a time interval to be _closer_ to what was actually requested /// base on the actual timebase frequency. #define PK_INTERVAL_SCALE(interval) ((interval) + ((interval) >> __pk_timebase_rshift)) /// Convert a time in integral seconds to a time interval - overflows are /// ignored. The application can redefine this macro. #ifndef PK_SECONDS #define PK_SECONDS(s) ((PkInterval)(PK_BASE_FREQ_HZ * (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 PK_MILLISECONDS #define PK_MILLISECONDS(m) ( (PkInterval)(PK_BASE_FREQ_KHZ * (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 PK_MICROSECONDS #define PK_MICROSECONDS(u) ( (PkInterval)(PK_BASE_FREQ_MHZ * (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 PK_NANOSECONDS #define PK_NANOSECONDS(n) ( (PkInterval)( ( ((PK_BASE_FREQ_MHZ<<10)/1000) * (n) ) >> 10) ) #endif /// Enable PK application tracing (enabled by default) #ifndef PK_TRACE_ENABLE #define PK_TRACE_ENABLE 1 #endif /// Enable PK kernel tracing (disabled by default) #ifndef PK_KERNEL_TRACE_ENABLE #define PK_KERNEL_TRACE_ENABLE 0 #endif //Application trace macros #if !PK_TRACE_ENABLE #define PK_TRACE(...) #define PK_TRACE_BIN(str, bufp, buf_size) #else #define PK_TRACE(...) PKTRACE(__VA_ARGS__) #define PK_TRACE_BIN(str, bufp, buf_size) PKTRACE_BIN(str, bufp, buf_size) #endif //Kernel trace macros #if !PK_KERNEL_TRACE_ENABLE #define PK_KERN_TRACE(...) #define PK_KERN_TRACE_ASM16(...) #else #define PK_KERN_TRACE(...) PK_TRACE(__VA_ARGS__) #define PK_KERN_TRACE_ASM16(...) PK_TRACE_ASM16(__VA_ARGS__) #endif /* PK_KERNEL_TRACE_ENABLE */ /// Add a string to the trace buffer with an optional register holding a 16bit value /// WARNING: This calls a c function which may clobber any of the volatile registers #if (PK_TRACE_SUPPORT && PK_TIMER_SUPPORT) #define PK_TRACE_ASM16(...) TRACE_ASM_HELPER16(VARG_COUNT(__VA_ARGS__), __VA_ARGS__) #else #define PK_TRACE_ASM16(...) #endif /* PK_TRACE_SUPPORT */ /// The following macros are helper macros for tracing. They should not be called /// directly. #define VARG_COUNT_HELPER(_0, _1, _2, _3, _4, _5, _6, _7, N, ...) N #define VARG_COUNT(...) VARG_COUNT_HELPER(, ##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0) #ifdef __ASSEMBLER__ #define TRACE_ASM_HELPER16_CALL(count, ...) TINY_TRACE_ASM ## count (__VA_ARGS__) #define TRACE_ASM_HELPER16(count, ...) TRACE_ASM_HELPER16_CALL(count, __VA_ARGS__) #define TINY_TRACE_ASM0() .error "format string required" #define TINY_TRACE_ASM1(str) \ .tiny_trace_asm1 trace_ppe_hash(str, PK_TRACE_HASH_PREFIX) #define TINY_TRACE_ASM2(str, reg) \ .tiny_trace_asm2 trace_ppe_hash(str, PK_TRACE_HASH_PREFIX), reg #define TINY_TRACE_ASM3() .error "too many parameters" #define TINY_TRACE_ASM4() .error "too many parameters" #define TINY_TRACE_ASM5() .error "too many parameters" #define TINY_TRACE_ASM6() .error "too many parameters" #define TINY_TRACE_ASM7() .error "too many parameters" //TODO: add support for tracing more than 1 parameter and binary data in assembly .global pk_trace_tiny .macro .tiny_trace_asm1 hash16 lis %r3, \hash16 bl pk_trace_tiny .endm .macro .tiny_trace_asm2 hash16, parm16 clrlwi %r3, \parm16, 16 oris %r3, %r3, \hash16 bl pk_trace_tiny .endm #endif /*__ASSEMBLER__*/ #ifndef __ASSEMBLER__ #include #include /// The timebase frequency in Hz; A parameter to pk_initialize() extern uint32_t __pk_timebase_frequency_hz; extern uint8_t __pk_timebase_rshift; /// The timebase frequency in KHz extern uint32_t __pk_timebase_frequency_khz; /// The timebase frequency in Mhz extern uint32_t __pk_timebase_frequency_mhz; typedef unsigned long int PkAddress; typedef uint8_t PkThreadState; typedef uint8_t PkThreadPriority; typedef uint8_t PkThreadFlags; typedef uint32_t PkSemaphoreCount; typedef uint64_t PkTimebase; typedef PK_TIME_INTERVAL_TYPE PkInterval; #include "pk_port_types.h" typedef struct { /// A priority queue of threads pending on the semaphore. PkThreadQueue pending_threads; /// The current semaphore count. PkSemaphoreCount count; /// The maximum allowable count - for error checking. PkSemaphoreCount max_count; } PkSemaphore; /// Compile-time initialize a PkSemaphore structure /// /// This low-level macro creates a structure initializatin of an PkSemaphore /// structure. This can be used for example to create compile-time initialized /// arrays of semaphores. #define PK_SEMAPHORE_INITIALIZATION(_initial_count, _max_count) \ {.pending_threads = 0, \ .count = (_initial_count), \ .max_count = (_max_count)} /// Declare and initialize a semaphore #define PK_SEMAPHORE(sem, initial_count, max_count) \ PkSemaphore sem = PK_SEMAPHORE_INITIALIZATION(initial_count, max_count) /// Trace macros for C functions #define HASH_ARG_COMBO(str, arg) \ ((((uint32_t)trace_ppe_hash(str, PK_TRACE_HASH_PREFIX)) << 16) | ((uint32_t)(arg) & 0x0000ffff)) #define PKTRACE0(...) pk_trace_tiny() //will fail at compile time #define PKTRACE1(str) \ pk_trace_tiny((trace_ppe_hash(str, PK_TRACE_HASH_PREFIX) << 16)) #define PKTRACE2(str, parm0) \ ((sizeof(parm0) <= 2)? \ pk_trace_tiny(HASH_ARG_COMBO(str, parm0)): \ pk_trace_big(HASH_ARG_COMBO(str, 1), ((uint64_t)parm0) << 32, 0)) #define PKTRACE3(str, parm0, parm1) \ pk_trace_big(HASH_ARG_COMBO(str, 2), ((((uint64_t)parm0) << 32) | parm1), 0) #define PKTRACE4(str, parm0, parm1, parm2) \ pk_trace_big(HASH_ARG_COMBO(str, 3), ((((uint64_t)parm0) << 32) | parm1),\ ((uint64_t)parm2) << 32 ) #define PKTRACE5(str, parm0, parm1, parm2, parm3) \ pk_trace_big(HASH_ARG_COMBO(str, 4), ((((uint64_t)parm0) << 32) | parm1),\ ((((uint64_t)parm2) << 32) | parm3) ) #define PKTRACE6(...) pk_trace_tiny() //will fail at compile time #define PKTRACE7(...) pk_trace_tiny() //will fail at compile time #define PKTRACE_HELPER2(count, ...) PKTRACE ## count (__VA_ARGS__) #define PKTRACE_HELPER(count, ...) PKTRACE_HELPER2(count, __VA_ARGS__) #if (PK_TRACE_SUPPORT && PK_TIMER_SUPPORT) #define PKTRACE(...) PKTRACE_HELPER(VARG_COUNT(__VA_ARGS__), __VA_ARGS__) #define PKTRACE_BIN(str, bufp, buf_size) \ pk_trace_binary(((buf_size < 255)? HASH_ARG_COMBO(str, buf_size): HASH_ARG_COMBO(str, 255)), bufp) #else #define PKTRACE(...) #define PKTRACE_BIN(str, bufp, buf_size) #endif //PK_TRACE_SUPPORT /// 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 PK API assumes that /// queueable structures will be defined with an PkDeque 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 PkDeque and vice-versa. typedef struct PkDeque { /// Pointer to the head or the next element in a deque. /// /// When an PkDeque is used as the sentinel node for a queue, \a next /// points to the head of the queue, and the condition (next == \) /// indicates an empty PkDeque. By convention the condition (\a next == /// 0) is used to indicate that a queue element is not enqueued. struct PkDeque* 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 PkDeque* previous; } PkDeque; typedef void (*PkTimerCallback)(void *); #define PK_TIMER_CALLBACK(callback) void callback(void *) struct PkTimer; /// The PK timer object typedef struct PkTimer { /// The time queue management pointers /// /// This pointer container is defined as the first element of the /// structure to allow the PkTimer to be cast to an PkDeque and /// vice-versa. PkDeque deque; /// The absolute timeout of the timer. PkTimebase timeout; /// The timer callback /// /// For PK thread timers used to implement Sleep and semaphore pend /// timeouts this field is initialized to __pk_thread_timeout(). PkTimerCallback callback; /// Private data passed to the callback. /// /// For PK thread timers used to implement Sleep and semaphore pend this /// field is initialized to a pointer to the thread. void *arg; } PkTimer; // Threads typedef void (*PkThreadRoutine)(void *arg); #define PK_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. PkAddress 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 /// PkAddress) from the thread pointer. PkAddress stack_limit; /// This is the original base of the stack. /// Assembler code expects this to always be at address offset 2 * (sizeof /// PkAddress) from the thread pointer. PkAddress stack_base; /// If the thread is blocked on a semaphore, then this is the semaphore the /// thread is blocked on. PkSemaphore *semaphore; /// The thread priority. PkThreadPriority priority; /// The thread state; See \ref pk_thread_states PkThreadState state; /// Thread flags; See \ref pk_thread_flags PkThreadFlags flags; /// The timer structure handles Sleep and blocking on a semaphore with /// timeout. PkTimer timer; } PkThread; typedef void (*PkBhHandler)(void *); #define PK_BH_HANDLER(handler) void handler(void *) typedef struct { /// The bottom half queue management pointers /// /// This pointer container is defined as the first element of the /// structure to allow the PkBottomHalf to be cast to a PkDeque and /// vice-versa. PkDeque deque; /// The bottom half handler PkBhHandler bh_handler; /// Private data passed to the handler. void *arg; } PkBottomHalf; // Initialization APIs int pk_initialize(PkAddress kernel_stack, size_t kernel_stack_size, PkTimebase initial_timebase, uint32_t timebase_frequency_hz); // Timebase APIs PkTimebase pk_timebase_get(void); // Timer APIs int pk_timer_create(PkTimer *timer, PkTimerCallback callback, void *arg); int pk_timer_schedule(PkTimer *timer, PkInterval interval); int pk_timer_cancel(PkTimer *timer); int pk_timer_info_get(PkTimer *timer, PkTimebase *timeout, int *active); // Thread APIs int pk_thread_create(PkThread *thread, PkThreadRoutine thread_routine, void *arg, PkAddress stack, size_t stack_size, PkThreadPriority priority); int pk_start_threads(void); int pk_thread_resume(PkThread *thread); int pk_thread_suspend(PkThread *thread); int pk_thread_delete(PkThread *thread); int pk_complete(void); int pk_sleep(PkInterval interval); int pk_thread_info_get(PkThread *thread, PkThreadState *state, PkThreadPriority *priority, int *runnable); int pk_thread_priority_change(PkThread *thread, PkThreadPriority new_priority, PkThreadPriority *old_priority); int pk_thread_at_priority(PkThreadPriority priority, PkThread **thread); int pk_thread_priority_swap(PkThread* thread_a, PkThread* thread_b); // Semaphore APIs int pk_semaphore_create(PkSemaphore *semaphore, PkSemaphoreCount initial_count, PkSemaphoreCount max_count); int pk_semaphore_post(PkSemaphore *semaphore); int pk_semaphore_pend(PkSemaphore *semaphore, PkInterval timeout); int pk_semaphore_release_all(PkSemaphore *semaphore); int pk_semaphore_info_get(PkSemaphore *semaphore, PkSemaphoreCount *count, int *pending); void pk_semaphore_post_handler(void *arg, PkIrqId irq); // Misc. APIs void pk_halt() __attribute__ ((noreturn)); // Deque APIs int pk_deque_sentinel_create(PkDeque *deque); #define PK_DEQUE_SENTINEL_INIT(dq_addr) \ {\ .next = dq_addr, \ .previous = dq_addr \ } #define PK_DEQUE_SENTINEL_STATIC_CREATE(deque) \ PkDeque deque = PK_DEQUE_SENTINEL_INIT(&deque) int pk_deque_element_create(PkDeque *element); #define PK_DEQUE_ELEMENT_INIT() \ {\ .next = 0, \ .previous = 0 \ } #define PK_DEQUE_ELEMENT_STATIC_CREATE(deque) \ PkDeque deque = PK_DEQUE_ELEMENT_INIT() /// Check for an empty PkDeque /// /// \param deque The sentinel node of a deque /// /// \retval 0 The PkDeque is not empty /// /// \retval 1 The PkDeque is empty static inline int pk_deque_is_empty(PkDeque *deque) { return (deque == deque->next); } /// Check if an PkDeque element is currently enqueued /// /// \param element Typically the PkDeque object of a queable structure /// /// \retval 0 The element is not currently enqueued /// /// \retval 1 The element is currently enqueued static inline int pk_deque_is_queued(PkDeque *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 PkDeque 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 pk_deque_push_back(PkDeque *deque, PkDeque *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 PkDeque 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 pk_deque_push_front(PkDeque *deque, PkDeque *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 PkDeque 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 PkDeque * pk_deque_pop_front(PkDeque *deque) { PkDeque *head; if (pk_deque_is_empty(deque)) { return 0; } else { head = (PkDeque *)(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 PkDeque 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 pk_deque_delete(PkDeque *element) { element->previous->next = element->next; element->next->previous = element->previous; element->next = 0; } // Bottom Half APIs extern PkDeque _pk_bh_queue; static inline void pk_bh_schedule(PkBottomHalf *bottom_half) { pk_deque_push_back(&_pk_bh_queue, (PkDeque *)bottom_half); } #define PK_BH_INIT(_handler, _arg) \ {\ .deque = PK_DEQUE_ELEMENT_INIT(), \ .bh_handler = _handler, \ .arg = _arg \ } #define PK_BH_STATIC_CREATE(bh_name, handler, arg) \ PkBottomHalf bh_name = PK_BH_INIT(handler, arg) //Trace function prototypes void pk_trace_tiny(uint32_t i_parm); void pk_trace_big(uint32_t i_hash_and_count, uint64_t i_parm1, uint64_t i_parm2); void pk_trace_binary(uint32_t i_hash_and_size, void* bufp); void pk_trace_set_timebase(PkTimebase timebase); /// Cast a pointer to another type, in a way that won't cause warnings #define PK_CAST_POINTER(t, p) ((t)((PkAddress)(p))) // Static Assert Macro for Compile time assertions. // - This macro can be used both inside and outside of a function. // - A value of false will cause the ASSERT to produce this error // - This will show up on a compile fail as: // : error: size of array '_static_assert' is negative // - It would be trivial to use the macro to paste a more descriptive // array name for each assert, but we will leave it like this for now. #define PK_STATIC_ASSERT(cond) extern uint8_t _static_assert[(cond) ? 1 : -1] __attribute__ ((unused)) /// \page pk_errors PK API and Kernel Error Handling /// /// Error checking in the PK 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 /// PK 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 PK default is to check for API errors and kernel /// invariants, and panic should errors occur. /// /// PK follows the Unix convention that a successful call of an API returns 0 /// (PK_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 PK 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 /// PK API and kernel: /// /// \c PK_ERROR_CHECK_API /// /// \arg \b 0 - No PK API error checking. APIs that potentially return error /// codes will always return 0 (PK_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 PK API errors are checked. The behavior in /// the event of an error is defined by the configuration option /// PK_ERROR_PANIC. /// /// \c PK_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 PK_ERROR_PANIC /// /// \arg \b 0 - PK API calls return negative error codes in the event of /// errors. Note that PK kernel invariants always cause a panic if /// violations occur. /// /// \arg \b 1 - (Default) In the event of errors PK APIs invoke PK_PANIC(code), /// where code is a positive error code. Kernel invariant checks always /// cause a panic if violations are detected. #endif /* __ASSEMBLER__ */ #endif /* __PK_API_H__ */