From 90a35847498bb68ec1dbd0403c577ea1b7895d0f Mon Sep 17 00:00:00 2001 From: David Vrabel Date: Wed, 4 Mar 2020 18:00:41 +0000 Subject: [PATCH] portable/GCC/Posix: add new port for Posix (Linux) applications This is similar to the Windows port, allowing FreeRTOS kernel applications to run as regular applications on Posix (Linux) systems. You can use this in a 32-bit or 64-bit application (although there are dynamic memory allocation trace points that do not support 64-bit addresses). Many of the same caveats of running an RTOS on a non-real-time system apply, but this is still very useful for easy debugging/testing applications in a simulated environment. In particular, it allows easy use of tools such as valgrind. You can call standard library functions from tasks but care must be taken with any that internally take mutexes or block. This includes malloc()/free() and many stdio functions (e.g., printf()). Replacement malloc(), free(), realloc(), and calloc() functions are provided which are safe. printf() needs to be called with a FreeRTOS mutex help (or called from only a single task). Each task is run in its own pthread, which makes debugging with standard tools (such as GDB) easier backtraces for individual tasks are available. Threads for non-running tasks are blocked in sigwait(). The stack for each task (thread) is allocated when the thread is created, and the stack provided during task creation is not used. This is so the stack has guard pages, to help with detecting stack overflows. Task switch is done by resuming the thread for the next task by sending it the resume signal (SIGUSR1) and then suspending the current thread. The timer interrupt uses SIGALRM and care is taken to ensure that the signal handler runs only on the thread for the current task. The additional data needed per-thread is stored at the top on the task's stack. When a running task is being deleted, its thread is marked it as dying so when we switch away from it it exits instead of suspending. This ensures that even if the idle task doesn't run, threads are deleted which allows for more threads to be created (if many tasks are being created and deleted in rapid succession). To further aid debugging, SIGINT (^C) is not blocked inside critical sections. This allows it to be used break into GDB while in a critical section. This means that care must be taken with any custom SIGINT handlers as these are like NMIs. This is somewhat inspired by an existing port by William Davy (https://www.freertos.org/FreeRTOS-simulator-for-Linux.html) but it takes a number of different approaches to make it switch tasks reliableand there's little similarly with the original implementation. - Critical sections block scheduling/"interrupts" by blocking signals using pthread_sigmask(). This is more expensive than attempting to use flags but works reliably and is analogous to the interrupt enable/disable on real hardware. - Care is take to ensure that the SIGALRM handler (for the timer tick) is runnable only on the pthread for the running task. This makes tasks switches more straight-forward and reliable as we can suspend the thread while in the signal handler. - Task switches save/restore the critical nesting on the stack. - Only uses a single (SIGUSR1) signal which is ignored and thus GDB's default signal handling options won't trap/print on this signal. - Extra per-thread data is stored on the task's stack, making it accessible in O(1) instead of performing a O(n) lookup of the array. - Uses the task create/delete hooks in a similar way to the Windows port, rather than overloading trace points. --- portable/ThirdParty/GCC/Posix/port.c | 601 ++++++++++++++++++++++ portable/ThirdParty/GCC/Posix/portmacro.h | 134 +++++ 2 files changed, 735 insertions(+) create mode 100644 portable/ThirdParty/GCC/Posix/port.c create mode 100644 portable/ThirdParty/GCC/Posix/portmacro.h diff --git a/portable/ThirdParty/GCC/Posix/port.c b/portable/ThirdParty/GCC/Posix/port.c new file mode 100644 index 000000000..bae6e2f84 --- /dev/null +++ b/portable/ThirdParty/GCC/Posix/port.c @@ -0,0 +1,601 @@ +/* + * FreeRTOS Kernel V10.3.0 + * Copyright (C) 2020 Cambridge Consultants Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/*----------------------------------------------------------- + * Implementation of functions defined in portable.h for the Posix port. + * + * Each task has a pthread which eases use of standard debuggers + * (allowing backtraces of tasks etc). Threads for tasks that are not + * running are blocked in sigwait(). + * + * Task switch is done by resuming the thread for the next task by + * sending it the resume signal (SIGUSR1) and then suspending the + * current thread. + * + * The timer interrupt uses SIGALRM and care is taken to ensure that + * the signal handler runs only on the thread for the current task. + * + * Use of part of the standard C library requires care as some + * functions can take pthread mutexes internally which can result in + * deadlocks as the FreeRTOS kernel can switch tasks while they're + * holding a pthread mutex. + * + * Replacement malloc(), free(), calloc(), and realloc() are provided + * for glibc (see below for more information). + * + * stdio (printf() and friends) should be called from a single task + * only or serialized with a FreeRTOS primitive such as a binary + * semaphore or mutex. + *----------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" +/*-----------------------------------------------------------*/ + +#define SIG_RESUME SIGUSR1 + +typedef struct THREAD +{ + pthread_t pthread; + pdTASK_CODE pxCode; + void *pvParams; + BaseType_t xDying; +} Thread_t; + +/* + * The additional per-thread data is stored at the beginning of the + * task's stack. + */ +static inline Thread_t *prvGetThreadFromTask(TaskHandle_t xTask) +{ +StackType_t *pxTopOfStack = *(StackType_t **)xTask; + + return (Thread_t *)(pxTopOfStack + 1); +} + +/*-----------------------------------------------------------*/ + +static pthread_once_t hSigSetupThread = PTHREAD_ONCE_INIT; +static sigset_t xResumeSignals; +static sigset_t xAllSignals; +static sigset_t xSchedulerOriginalSignalMask; +static pthread_t hMainThread = ( pthread_t )NULL; +static volatile portBASE_TYPE uxCriticalNesting; +/*-----------------------------------------------------------*/ + +static portBASE_TYPE xSchedulerEnd = pdFALSE; +/*-----------------------------------------------------------*/ + +static void prvSetupSignalsAndSchedulerPolicy( void ); +static void prvSetupTimerInterrupt( void ); +static void *prvWaitForStart( void * pvParams ); +static void prvSwitchThread( Thread_t *xThreadToResume, Thread_t *xThreadToSuspend ); +static void prvSuspendSelf( void ); +static void prvResumeThread( pthread_t xThreadId ); +static void vPortSystemTickHandler( int sig ); +static void vPortStartFirstTask( void ); +/*-----------------------------------------------------------*/ + +/* + * The standard glibc malloc(), free() etc. take an internal lock so + * it is not safe to switch tasks while calling them. + * + * Requiring the application use the safe xPortMalloc() and + * vPortFree() is not sufficient as malloc() is used internally by + * glibc (e.g., by strdup() and the pthread library.) + * + * To further complicate things malloc() and free() may be called + * outside of task context during pthread destruction so using + * vTaskSuspend() and xTaskResumeAll() cannot be used. + * vPortEnterCritical() and vPortExitCritical() cannot be used either + * as they use global state for the critical section nesting (this + * cannot be fixed by using TLS as pthread destruction needs to free + * the TLS). + * + * Explicitly save/disable and restore the signal mask to block the + * timer (SIGALRM) and other signals. + */ + +extern void *__libc_malloc(size_t); +extern void __libc_free(void *); +extern void *__libc_calloc(size_t, size_t); +extern void *__libc_realloc(void *ptr, size_t); + +void *malloc(size_t size) +{ +sigset_t xSavedSignals; +void *ptr; + + pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals ); + ptr = __libc_malloc( size ); + pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL ); + + return ptr; +} + +void free(void *ptr) +{ +sigset_t xSavedSignals; + + pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals ); + __libc_free( ptr ); + pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL ); +} + +void *calloc(size_t nmemb, size_t size) +{ +sigset_t xSavedSignals; +void *ptr; + + pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals ); + ptr = __libc_calloc( nmemb, size ); + pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL ); + + return ptr; +} + +void *realloc(void *ptr, size_t size) +{ +sigset_t xSavedSignals; + + pthread_sigmask( SIG_BLOCK, &xAllSignals, &xSavedSignals ); + ptr = __libc_realloc( ptr, size ); + pthread_sigmask( SIG_SETMASK, &xSavedSignals, NULL ); + + return ptr; +} + +static void prvFatalError( const char *pcCall, int iErrno ) +{ + fprintf( stderr, "%s: %s\n", pcCall, strerror( iErrno ) ); + abort(); +} + +/* + * See header file for description. + */ +portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, + portSTACK_TYPE *pxEndOfStack, + pdTASK_CODE pxCode, void *pvParameters ) +{ +Thread_t *thread; +pthread_attr_t xThreadAttributes; +size_t ulStackSize; +int iRet; + + (void)pthread_once( &hSigSetupThread, prvSetupSignalsAndSchedulerPolicy ); + + /* + * Store the additional thread data at the start of the stack. + */ + thread = (Thread_t *)(pxTopOfStack + 1) - 1; + pxTopOfStack = (portSTACK_TYPE *)thread - 1; + ulStackSize = (pxTopOfStack + 1 - pxEndOfStack) * sizeof(*pxTopOfStack); + + thread->pxCode = pxCode; + thread->pvParams = pvParameters; + thread->xDying = pdFALSE; + + pthread_attr_init( &xThreadAttributes ); + pthread_attr_setstack( &xThreadAttributes, pxEndOfStack, ulStackSize ); + + vPortEnterCritical(); + + iRet = pthread_create( &thread->pthread, &xThreadAttributes, + prvWaitForStart, thread ); + if ( iRet ) + { + prvFatalError( "pthread_create", iRet ); + } + + vPortExitCritical(); + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +void vPortStartFirstTask( void ) +{ +Thread_t *pxFirstThread = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() ); + + /* Start the first task. */ + prvResumeThread( pxFirstThread->pthread ); +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +portBASE_TYPE xPortStartScheduler( void ) +{ +int iSignal; +sigset_t xSignals; + + hMainThread = pthread_self(); + + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + prvSetupTimerInterrupt(); + + /* Start the first task. */ + vPortStartFirstTask(); + + /* Wait until signaled by vPortEndScheduler(). */ + sigemptyset( &xSignals ); + sigaddset( &xSignals, SIG_RESUME ); + + while ( !xSchedulerEnd ) + { + sigwait( &xSignals, &iSignal ); + } + + /* Restore original signal mask. */ + (void)pthread_sigmask( SIG_SETMASK, &xSchedulerOriginalSignalMask, NULL ); + + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ +struct itimerval itimer; +struct sigaction sigtick; + + /* Stop the timer and ignore any pending SIGALRMs that would end + * up running on the main thread when it is resumed. */ + itimer.it_value.tv_sec = 0; + itimer.it_value.tv_usec = 0; + (void)setitimer( ITIMER_REAL, &itimer, NULL ); + + sigtick.sa_flags = 0; + sigtick.sa_handler = SIG_IGN; + sigaction( SIGALRM, &sigtick, NULL ); + + /* Signal the scheduler to exit its loop. */ + xSchedulerEnd = pdTRUE; + (void)pthread_kill( hMainThread, SIG_RESUME ); + + prvSuspendSelf(); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + if ( uxCriticalNesting == 0 ) + { + vPortDisableInterrupts(); + } + uxCriticalNesting++; +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + uxCriticalNesting--; + + /* If we have reached 0 then re-enable the interrupts. */ + if( uxCriticalNesting == 0 ) + { + vPortEnableInterrupts(); + } +} +/*-----------------------------------------------------------*/ + +void vPortYieldFromISR( void ) +{ +Thread_t *xThreadToSuspend; +Thread_t *xThreadToResume; + + xThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() ); + + vTaskSwitchContext(); + + xThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() ); + + prvSwitchThread( xThreadToResume, xThreadToSuspend ); +} +/*-----------------------------------------------------------*/ + +void vPortYield( void ) +{ + vPortEnterCritical(); + + vPortYieldFromISR(); + + vPortExitCritical(); +} +/*-----------------------------------------------------------*/ + +void vPortDisableInterrupts( void ) +{ + pthread_sigmask( SIG_BLOCK, &xAllSignals, NULL ); +} +/*-----------------------------------------------------------*/ + +void vPortEnableInterrupts( void ) +{ + pthread_sigmask( SIG_UNBLOCK, &xAllSignals, NULL ); +} +/*-----------------------------------------------------------*/ + +portBASE_TYPE xPortSetInterruptMask( void ) +{ + /* Interrupts are always disabled inside ISRs (signals + handlers). */ + return pdTRUE; +} +/*-----------------------------------------------------------*/ + +void vPortClearInterruptMask( portBASE_TYPE xMask ) +{ +} +/*-----------------------------------------------------------*/ + +static uint64_t prvGetTimeNs(void) +{ +struct timespec t; + + clock_gettime(CLOCK_MONOTONIC, &t); + + return t.tv_sec * 1000000000ull + t.tv_nsec; +} + +static uint64_t prvStartTimeNs; +static uint64_t prvTickCount; + +/* + * Setup the systick timer to generate the tick interrupts at the required + * frequency. + */ +void prvSetupTimerInterrupt( void ) +{ +struct itimerval itimer; +int iRet; + + /* Initialise the structure with the current timer information. */ + iRet = getitimer( ITIMER_REAL, &itimer ); + if ( iRet ) + { + prvFatalError( "getitimer", errno ); + } + + /* Set the interval between timer events. */ + itimer.it_interval.tv_sec = 0; + itimer.it_interval.tv_usec = portTICK_RATE_MICROSECONDS; + + /* Set the current count-down. */ + itimer.it_value.tv_sec = 0; + itimer.it_value.tv_usec = portTICK_RATE_MICROSECONDS; + + /* Set-up the timer interrupt. */ + iRet = setitimer( ITIMER_REAL, &itimer, NULL ); + if ( iRet ) + { + prvFatalError( "setitimer", errno ); + } + + prvStartTimeNs = prvGetTimeNs(); +} +/*-----------------------------------------------------------*/ + +static void vPortSystemTickHandler( int sig ) +{ +Thread_t *pxThreadToSuspend; +Thread_t *pxThreadToResume; +uint64_t xExpectedTicks; + + uxCriticalNesting++; /* Signals are blocked in this signal handler. */ + + pxThreadToSuspend = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() ); + + /* Tick Increment, accounting for any lost signals or drift in + * the timer. */ + xExpectedTicks = (prvGetTimeNs() - prvStartTimeNs) + / (portTICK_RATE_MICROSECONDS * 1000); + do { + xTaskIncrementTick(); + prvTickCount++; + } while (prvTickCount < xExpectedTicks); + +#if ( configUSE_PREEMPTION == 1 ) + /* Select Next Task. */ + vTaskSwitchContext(); + + pxThreadToResume = prvGetThreadFromTask( xTaskGetCurrentTaskHandle() ); + + prvSwitchThread(pxThreadToResume, pxThreadToSuspend); +#endif + + uxCriticalNesting--; +} +/*-----------------------------------------------------------*/ + +void vPortThreadDying( void *pxTaskToDelete, volatile BaseType_t *pxPendYield ) +{ +Thread_t *pxThread = prvGetThreadFromTask( pxTaskToDelete ); + + pxThread->xDying = pdTRUE; +} + +void vPortCancelThread( void *pxTaskToDelete ) +{ +Thread_t *pxThreadToCancel = prvGetThreadFromTask( pxTaskToDelete ); + + /* + * The thread has already been suspended so it can be safely + * cancelled. + */ + pthread_cancel( pxThreadToCancel->pthread ); + pthread_join( pxThreadToCancel->pthread, NULL ); +} +/*-----------------------------------------------------------*/ + +static void *prvWaitForStart( void * pvParams ) +{ +Thread_t *pxThread = pvParams; + + prvSuspendSelf(); + + /* Resumed for the first time, unblocks all signals. */ + uxCriticalNesting = 0; + vPortEnableInterrupts(); + + /* Call the task's entry point. */ + pxThread->pxCode( pxThread->pvParams ); + + return NULL; +} +/*-----------------------------------------------------------*/ + +static void prvSwitchThread( Thread_t *pxThreadToResume, + Thread_t *pxThreadToSuspend ) +{ +BaseType_t uxSavedCriticalNesting; + + if ( pxThreadToSuspend != pxThreadToResume ) + { + /* + * Switch tasks. + * + * The critical section nesting is per-task, so save it on the + * stack of the current (suspending thread), restoring it when + * we switch back to this task. + */ + uxSavedCriticalNesting = uxCriticalNesting; + + prvResumeThread( pxThreadToResume->pthread ); + if ( pxThreadToSuspend->xDying ) + { + pthread_exit( NULL ); + } + prvSuspendSelf(); + + uxCriticalNesting = uxSavedCriticalNesting; + } +} +/*-----------------------------------------------------------*/ + +static void prvSuspendSelf( void ) +{ +int iSig; + + /* + * Suspend this thread by waiting for a SIG_RESUME signal. + * + * A suspended thread must not handle signals (interrupts) so + * all signals must be blocked by calling this from: + * + * - Inside a critical section (vPortEnterCritical() / + * vPortExitCritical()). + * + * - From a signal handler that has all signals masked. + * + * - A thread with all signals blocked with pthread_sigmask(). + */ + sigwait( &xResumeSignals, &iSig ); +} + +/*-----------------------------------------------------------*/ + +static void prvResumeThread( pthread_t xThreadId ) +{ + if ( pthread_self() != xThreadId ) + { + pthread_kill( xThreadId, SIG_RESUME ); + } +} +/*-----------------------------------------------------------*/ + +static void prvSetupSignalsAndSchedulerPolicy( void ) +{ +struct sigaction sigresume, sigtick; +int iRet; + + hMainThread = pthread_self(); + + /* Initialise common signal masks. */ + sigemptyset( &xResumeSignals ); + sigaddset( &xResumeSignals, SIG_RESUME ); + sigfillset( &xAllSignals ); + /* Don't block SIGINT so this can be used to break into GDB while + * in a critical section. */ + sigdelset( &xAllSignals, SIGINT ); + + /* + * Block all signals in this thread so all new threads + * inherits this mask. + * + * When a thread is resumed for the first time, all signals + * will be unblocked. + */ + (void)pthread_sigmask( SIG_SETMASK, &xAllSignals, + &xSchedulerOriginalSignalMask ); + + /* SIG_RESUME is only used with sigwait() so doesn't need a + handler. */ + sigresume.sa_flags = 0; + sigresume.sa_handler = SIG_IGN; + sigfillset( &sigresume.sa_mask ); + + sigtick.sa_flags = 0; + sigtick.sa_handler = vPortSystemTickHandler; + sigfillset( &sigtick.sa_mask ); + + iRet = sigaction( SIG_RESUME, &sigresume, NULL ); + if ( iRet ) + { + prvFatalError( "sigaction", errno ); + } + + iRet = sigaction( SIGALRM, &sigtick, NULL ); + if ( iRet ) + { + prvFatalError( "sigaction", errno ); + } +} +/*-----------------------------------------------------------*/ + +unsigned long ulPortGetRunTime( void ) +{ +struct tms xTimes; + + times( &xTimes ); + + return ( unsigned long ) xTimes.tms_utime; +} +/*-----------------------------------------------------------*/ diff --git a/portable/ThirdParty/GCC/Posix/portmacro.h b/portable/ThirdParty/GCC/Posix/portmacro.h new file mode 100644 index 000000000..8046cbc8f --- /dev/null +++ b/portable/ThirdParty/GCC/Posix/portmacro.h @@ -0,0 +1,134 @@ +/* + * FreeRTOS Kernel V10.3.0 + * Copyright 2020 Cambridge Consultants Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE unsigned long +#define portBASE_TYPE long +#define portPOINTER_SIZE_TYPE intptr_t + +typedef portSTACK_TYPE StackType_t; +typedef long BaseType_t; +typedef unsigned long UBaseType_t; + +typedef unsigned long TickType_t; +#define portMAX_DELAY ( TickType_t ) ULONG_MAX + +#define portTICK_TYPE_IS_ATOMIC 1 + +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portHAS_STACK_OVERFLOW_CHECKING ( 1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portTICK_RATE_MICROSECONDS ( ( portTickType ) 1000000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 8 +/*-----------------------------------------------------------*/ + +/* Scheduler utilities. */ +extern void vPortYield( void ); + +#define portYIELD() vPortYield() + +#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) vPortYield() +#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + +/* Critical section management. */ +extern void vPortDisableInterrupts( void ); +extern void vPortEnableInterrupts( void ); +#define portSET_INTERRUPT_MASK() ( vPortDisableInterrupts() ) +#define portCLEAR_INTERRUPT_MASK() ( vPortEnableInterrupts() ) + +extern portBASE_TYPE xPortSetInterruptMask( void ); +extern void vPortClearInterruptMask( portBASE_TYPE xMask ); + +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); +#define portSET_INTERRUPT_MASK_FROM_ISR() xPortSetInterruptMask() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortClearInterruptMask(x) +#define portDISABLE_INTERRUPTS() portSET_INTERRUPT_MASK() +#define portENABLE_INTERRUPTS() portCLEAR_INTERRUPT_MASK() +#define portENTER_CRITICAL() vPortEnterCritical() +#define portEXIT_CRITICAL() vPortExitCritical() + +/*-----------------------------------------------------------*/ + +extern void vPortThreadDying( void *pxTaskToDelete, volatile BaseType_t *pxPendYield ); +extern void vPortCancelThread( void *pxTaskToDelete ); +#define portPRE_TASK_DELETE_HOOK( pvTaskToDelete, pxPendYield ) vPortThreadDying( ( pvTaskToDelete ), ( pxPendYield ) ) +#define portCLEAN_UP_TCB( pxTCB ) vPortCancelThread( pxTCB ) +/*-----------------------------------------------------------*/ + +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) +/*-----------------------------------------------------------*/ + +/* + * Tasks run in their own pthreads and context switches between them + * are always a full memory barrier. ISRs are emulated as signals + * which also imply a full memory barrier. + * + * Thus, only a compilier barrier is needed to prevent the compiler + * reordering. + */ +#define portMEMORY_BARRIER() __asm volatile( "" ::: "memory" ) + +extern unsigned long ulPortGetRunTime( void ); +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() /* no-op */ +#define portGET_RUN_TIME_COUNTER_VALUE() ulPortGetRunTime() + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */