2 FreeRTOS V6.1.0 - Copyright (C) 2010 Real Time Engineers Ltd.
\r
4 ***************************************************************************
\r
8 * + New to FreeRTOS, *
\r
9 * + Wanting to learn FreeRTOS or multitasking in general quickly *
\r
10 * + Looking for basic training, *
\r
11 * + Wanting to improve your FreeRTOS skills and productivity *
\r
13 * then take a look at the FreeRTOS books - available as PDF or paperback *
\r
15 * "Using the FreeRTOS Real Time Kernel - a Practical Guide" *
\r
16 * http://www.FreeRTOS.org/Documentation *
\r
18 * A pdf reference manual is also available. Both are usually delivered *
\r
19 * to your inbox within 20 minutes to two hours when purchased between 8am *
\r
20 * and 8pm GMT (although please allow up to 24 hours in case of *
\r
21 * exceptional circumstances). Thank you for your support! *
\r
23 ***************************************************************************
\r
25 This file is part of the FreeRTOS distribution.
\r
27 FreeRTOS is free software; you can redistribute it and/or modify it under
\r
28 the terms of the GNU General Public License (version 2) as published by the
\r
29 Free Software Foundation AND MODIFIED BY the FreeRTOS exception.
\r
30 ***NOTE*** The exception to the GPL is included to allow you to distribute
\r
31 a combined work that includes FreeRTOS without being obliged to provide the
\r
32 source code for proprietary components outside of the FreeRTOS kernel.
\r
33 FreeRTOS is distributed in the hope that it will be useful, but WITHOUT
\r
34 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
\r
35 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
\r
36 more details. You should have received a copy of the GNU General Public
\r
37 License and the FreeRTOS license exception along with FreeRTOS; if not it
\r
38 can be viewed here: http://www.freertos.org/a00114.html and also obtained
\r
39 by writing to Richard Barry, contact details for whom are available on the
\r
44 http://www.FreeRTOS.org - Documentation, latest information, license and
\r
47 http://www.SafeRTOS.com - A version that is certified for use in safety
\r
50 http://www.OpenRTOS.com - Commercial support, development, porting,
\r
51 licensing and training services.
\r
54 /* Scheduler includes. */
\r
55 #include "FreeRTOS.h"
\r
59 //FILE *pfTraceFile = NULL;
\r
60 //#define vPortTrace( x ) if( pfTraceFile == NULL ) pfTraceFile = fopen( "c:/temp/trace.txt", "w" ); if( pfTraceFile != NULL ) fprintf( pfTraceFile, x )
\r
61 #define vPortTrace( x ) ( void ) x
\r
63 #define portMAX_INTERRUPTS ( ( unsigned long ) sizeof( unsigned long ) * 8UL ) /* The number of bits in an unsigned long. */
\r
64 #define portNO_CRITICAL_NESTING ( ( unsigned long ) 0 )
\r
67 * Created as a high priority thread, this function uses a timer to simulate
\r
68 * a tick interrupt being generated on an embedded target. In this Windows
\r
69 * environment the timer does not achieve real time performance though.
\r
71 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter );
\r
74 * Process all the simulated interrupts - each represented by a bit in
\r
75 * ulPendingInterrupts variable.
\r
77 static void prvProcessEvents( void );
\r
79 /*-----------------------------------------------------------*/
\r
81 /* The WIN32 simulator runs each task in a thread. The context switching is
\r
82 managed by the threads, so the task stack does not have to be managed directly,
\r
83 although the task stack is still used to hold an xThreadState structure this is
\r
84 the only thing it will ever hold. The structure indirectly maps the task handle
\r
85 to a thread handle. */
\r
88 /* Set to true for tasks that call the generate psuedo interrupt function,
\r
89 as the event handler needs to know whether to signal the interrupt ack
\r
90 event when the task next runs. */
\r
91 long lWaitingInterruptAck;
\r
93 /* Critical nesting count of the task - each task has its own. */
\r
94 portSTACK_TYPE ulCriticalNesting;
\r
96 /* Handle of the thread that executes the task. */
\r
100 /* Pseudo interrupts waiting to be processed. This is a bit mask where each
\r
101 bit represents one interrupt, so a maximum of 32 interrupts can be simulated. */
\r
102 static volatile unsigned long ulPendingInterrupts = 0UL;
\r
104 /* An event used to inform the interrupt dispatch thread (a high priority thread
\r
105 that simulated interrupt processing) that an IRQ or SWI type interrupt is
\r
107 static void *pvInterruptEvent = NULL;
\r
109 /* Mutex used to protect all the pseudo interrupt variables that are accessed by
\r
110 multiple threads. */
\r
111 static void *pvInterruptEventMutex = NULL;
\r
113 /* The main thread, which also acts as the pseudo interrupt handler. */
\r
114 static void *pvMainThreadAndInterrupHandler;
\r
116 /* Events used to manage sequencing. */
\r
117 static void *pvTickAcknowledgeEvent = NULL, *pvInterruptAcknowledgeEvent = NULL;
\r
119 /* The critical nesting count for the currently executing task. This is
\r
120 initialised to a non-zero value so interrupts do not become enabled during
\r
121 the initialisation phase. As each task has its own critical nesting value
\r
122 ulCriticalNesting will get set to zero when the first task runs. This
\r
123 initialisation is probably not critical in this simulated environment as the
\r
124 pseudo interrupt handlers/dispatchers do not get created until the FreeRTOS
\r
125 scheduler is started. */
\r
126 static unsigned portLONG ulCriticalNesting = 9999UL;
\r
128 /* Handlers for all the simulated software interrupts. The first two positions
\r
129 are used for the Yield and Tick interrupts so are handled slightly differently,
\r
130 all the other interrupts can be user defined. */
\r
131 static void (*vIsrHandler[ portMAX_INTERRUPTS ])( void ) = { 0 };
\r
133 /* Pointer to the TCB of the currently executing task. */
\r
134 extern void *pxCurrentTCB;
\r
136 /*-----------------------------------------------------------*/
\r
138 static DWORD WINAPI prvSimulatedPeripheralTimer( LPVOID lpParameter )
\r
140 /* Just to prevent compiler warnings. */
\r
141 ( void ) lpParameter;
\r
145 /* The timer is reset on each itteration of this loop rather than being set
\r
146 to function periodicallys - this is for the reasons stated in the comments
\r
147 where the timer is created. */
\r
148 vPortTrace( "prvSimulatedPeripheralTimer: Tick acked, re-Sleeping()\r\n" );
\r
150 /* Wait until the timer expires and we can access the pseudo interrupt
\r
152 Sleep( portTICK_RATE_MS );
\r
154 vPortTrace( "prvSimulatedPeripheralTimer: Timer signalled, waiting interrupt event mutex\r\n" );
\r
155 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
\r
156 vPortTrace( "prvSimulatedPeripheralTimer: Got interrupt event mutex\r\n" );
\r
158 /* The timer has expired, generate the simulated tick event. */
\r
159 ulPendingInterrupts |= ( 1 << portINTERRUPT_TICK );
\r
160 if( pvInterruptEvent != NULL )
\r
162 vPortTrace( "prvSimulatedPeripheralTimer: Setting interrupt event to signal tick\r\n" );
\r
163 SetEvent( pvInterruptEvent );
\r
166 /* Give back the mutex so the pseudo interrupt handler unblocks and can
\r
167 access the interrupt handler variables. This high priority task will then
\r
168 loop back round to wait for the lower priority psuedo interrupt handler
\r
169 thread to acknowledge the tick. */
\r
170 if( pvInterruptEventMutex != NULL )
\r
172 vPortTrace( "prvSimulatedPeripheralTimer: Releasing interrupt event mutex so tick can be processed\r\n" );
\r
173 ReleaseMutex( pvInterruptEventMutex );
\r
176 /* Wait for the previous tick to be acknowledged before resetting the timer.
\r
177 As mentioned above this is done to prevent timer overruns in the non real-
\r
178 time environment. THIS IS NOT HOW A REAL PORT SHOULD USE TIMERS! */
\r
179 WaitForSingleObject( pvTickAcknowledgeEvent, INFINITE );
\r
182 /*-----------------------------------------------------------*/
\r
184 portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters )
\r
186 xThreadState *pxThreadState = NULL;
\r
188 /* In this simulated case a stack is not initialised, but instead a thread
\r
189 is created that will execute the task being created. The thread handles
\r
190 the context switching itself. The xThreadState object is placed onto
\r
191 the stack that was created for the task - so the stack buffer is still
\r
192 used, just not in the conventional way. It will not be used for anything
\r
193 other than holding this structure. */
\r
194 pxThreadState = ( xThreadState * ) ( pxTopOfStack - sizeof( xThreadState ) );
\r
196 /* Create the thread itself. */
\r
197 pxThreadState->pvThread = ( void * ) CreateThread( NULL, 0, ( LPTHREAD_START_ROUTINE ) pxCode, pvParameters, CREATE_SUSPENDED, NULL );
\r
198 pxThreadState->ulCriticalNesting = portNO_CRITICAL_NESTING;
\r
199 pxThreadState->lWaitingInterruptAck = pdFALSE;
\r
200 SetThreadPriority( pxThreadState->pvThread, THREAD_PRIORITY_IDLE );
\r
202 return ( portSTACK_TYPE * ) pxThreadState;
\r
204 /*-----------------------------------------------------------*/
\r
206 portBASE_TYPE xPortStartScheduler( void )
\r
209 long lSuccess = pdPASS;
\r
210 xThreadState *pxThreadState;
\r
212 /* Set the priority of this thread such that it is above the priority of the
\r
213 threads that run tasks, but below the priority of the thread that generates
\r
214 the pseudo tick interrupts. This priority is chosen because this is the
\r
215 thread that actually handles the psuedo interrupts. */
\r
216 pvHandle = GetCurrentThread();
\r
217 if( pvHandle == NULL )
\r
222 if( lSuccess == pdPASS )
\r
224 if( SetThreadPriority( pvHandle, THREAD_PRIORITY_BELOW_NORMAL ) == 0 )
\r
230 if( lSuccess == pdPASS )
\r
232 /* Create the events and mutexes that are used to synchronise all the
\r
234 pvInterruptEventMutex = CreateMutex( NULL, FALSE, NULL );
\r
235 pvInterruptEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
\r
236 pvTickAcknowledgeEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
\r
237 pvInterruptAcknowledgeEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
\r
239 /* Start the thread that simulates the timer peripheral to generate
\r
240 tick interrupts. */
\r
241 pvHandle = CreateThread( NULL, 0, prvSimulatedPeripheralTimer, NULL, 0, NULL );
\r
242 if( pvHandle != NULL )
\r
244 SetThreadPriority( pvHandle, THREAD_PRIORITY_ABOVE_NORMAL );
\r
247 /* Start the highest priority task by obtaining its associated thread state
\r
248 structure, in which is stored the thread handle. */
\r
249 pxThreadState = ( xThreadState * ) *( ( unsigned long * ) pxCurrentTCB );
\r
250 ulCriticalNesting = portNO_CRITICAL_NESTING;
\r
252 vPortTrace( "Created system threads, starting task" );
\r
254 ResumeThread( pxThreadState->pvThread );
\r
257 /* Handle all pseudo interrupts - including yield requests and simulated ticks. */
\r
258 prvProcessEvents();
\r
260 /* Would not expect to return from prvProcessEvents(), so should not get here. */
\r
263 /*-----------------------------------------------------------*/
\r
265 static void prvProcessEvents( void )
\r
267 long lSwitchRequired;
\r
268 xThreadState *pxThreadState;
\r
269 void *pvObjectList[ 2 ];
\r
271 //char cTraceBuffer[ 256 ];
\r
273 vPortTrace( "Entering prvProcessEvents\r\n" );
\r
275 /* Going to block on the mutex that ensured exclusive access to the pdeudo
\r
276 interrupt objects, and the event that signals that an interrupt is waiting
\r
277 to be processed. */
\r
278 pvObjectList[ 0 ] = pvInterruptEventMutex;
\r
279 pvObjectList[ 1 ] = pvInterruptEvent;
\r
283 vPortTrace( "prvProcessEvents: Waiting for next interrupt event\r\n" );
\r
284 WaitForMultipleObjects( sizeof( pvObjectList ) / sizeof( void * ), pvObjectList, TRUE, INFINITE );
\r
285 vPortTrace( "prvProcessEvents: Got interrupt event and mutex\r\n" );
\r
286 //vPortTrace( "prvProcessEvents: Waiting for next interrupt event\r\n" );
\r
287 //WaitForSingleObject( pvInterruptEvent, INFINITE );
\r
288 //vPortTrace( "prvProcessEvents: Waiting interrupt event mutex to access interrupt data\r\n" );
\r
289 //WaitForSingleObject( pvInterruptEventMutex, INFINITE );
\r
291 lSwitchRequired = pdFALSE;
\r
293 /* For each interrupt we are interested in processing, each of which is
\r
294 represented by a bit in the 32bit ulPendingInterrupts variable. */
\r
295 for( i = 0; i < portMAX_INTERRUPTS; i++ )
\r
297 /* Is the pseudo interrupt pending? */
\r
298 if( ulPendingInterrupts & ( 1UL << i ) )
\r
302 case portINTERRUPT_YIELD:
\r
304 vPortTrace( "prvProcessEvents: Processing Yield\r\n" );
\r
305 /* Yield interrupts occur no matter what the critical nesting count. */
\r
306 lSwitchRequired = pdTRUE;
\r
308 /* Clear the interrupt pending bit. */
\r
309 ulPendingInterrupts &= ~( 1UL << portINTERRUPT_YIELD );
\r
312 case portINTERRUPT_TICK:
\r
314 /* Tick interrupts should only be processed if the critical nesting count
\r
315 is zero. The critical nesting count represents the interrupt mask on
\r
316 real target hardware. */
\r
317 vPortTrace( "prvProcessEvents: Processing tick event\r\n" );
\r
318 if( ulCriticalNesting == 0 )
\r
320 /* Process the tick itself. */
\r
321 vPortTrace( "prvProcessEvents: Incrementing tick\r\n" );
\r
322 vTaskIncrementTick();
\r
323 #if( configUSE_PREEMPTION != 0 )
\r
325 /* A context switch is only automatically performed from the tick
\r
326 interrupt if the pre-emptive scheduler is being used. */
\r
327 lSwitchRequired = pdTRUE;
\r
331 vPortTrace( "prvProcessEvents: Acking tick\r\n" );
\r
332 SetEvent( pvTickAcknowledgeEvent );
\r
334 /* Clear the interrupt pending bit. */
\r
335 ulPendingInterrupts &= ~( 1UL << portINTERRUPT_TICK );
\r
341 /* Is a handler installed? */
\r
342 if( vIsrHandler[ i ] != NULL )
\r
344 lSwitchRequired = pdTRUE;
\r
346 /* Run the actual handler. */
\r
347 vIsrHandler[ i ]();
\r
349 /* Clear the interrupt pending bit. */
\r
350 ulPendingInterrupts &= ~( 1UL << i );
\r
352 /* TODO: Need to have some sort of handshake event here for non-tick
\r
353 and none yield interrupts. */
\r
360 if( lSwitchRequired != pdFALSE )
\r
362 void *pvOldCurrentTCB;
\r
364 pvOldCurrentTCB = pxCurrentTCB;
\r
366 /* Save the state of the current thread before suspending it. */
\r
367 pxThreadState = ( xThreadState *) *( ( unsigned long * ) pxCurrentTCB );
\r
368 pxThreadState->ulCriticalNesting = ulCriticalNesting ;
\r
370 /* Select the next task to run. */
\r
371 vTaskSwitchContext();
\r
373 /* If the task selected to enter the running state is not the task
\r
374 that is already in the running state. */
\r
375 if( pvOldCurrentTCB != pxCurrentTCB )
\r
377 /* Suspend the old thread. */
\r
378 SuspendThread( pxThreadState->pvThread );
\r
379 //sprintf( cTraceBuffer, "Event processor: suspending %s, resuming %s\r\n", ((xTCB*)pvOldCurrentTCB)->pcTaskName, ((xTCB*)pxCurrentTCB)->pcTaskName );
\r
380 //vPortTrace( cTraceBuffer );
\r
382 /* Obtain the state of the task now selected to enter the Running state. */
\r
383 pxThreadState = ( xThreadState * ) ( *( unsigned long *) pxCurrentTCB );
\r
384 ulCriticalNesting = pxThreadState->ulCriticalNesting;
\r
385 ResumeThread( pxThreadState->pvThread );
\r
387 if( pxThreadState->lWaitingInterruptAck == pdTRUE )
\r
389 pxThreadState->lWaitingInterruptAck = pdFALSE;
\r
390 vPortTrace( "prvProcessEvents: Acking interrupt\r\n" );
\r
391 SetEvent( pvInterruptAcknowledgeEvent );
\r
396 ReleaseMutex( pvInterruptEventMutex );
\r
399 /*-----------------------------------------------------------*/
\r
401 void vPortEndScheduler( void )
\r
404 /*-----------------------------------------------------------*/
\r
406 void vPortGeneratePseudoInterrupt( unsigned long ulInterruptNumber )
\r
408 xThreadState *pxThreadState;
\r
410 if( ( ulInterruptNumber < portMAX_INTERRUPTS ) && ( pvInterruptEventMutex != NULL ) )
\r
412 /* Yield interrupts are processed even when critical nesting is non-zero. */
\r
413 if( ( ulCriticalNesting == 0 ) || ( ulInterruptNumber == portINTERRUPT_YIELD ) )
\r
415 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
\r
416 ulPendingInterrupts |= ( 1 << ulInterruptNumber );
\r
418 /* The event handler needs to know to signal the interrupt acknowledge event
\r
419 the next time this task runs. */
\r
420 pxThreadState = ( xThreadState * ) *( ( unsigned long * ) pxCurrentTCB );
\r
421 pxThreadState->lWaitingInterruptAck = pdTRUE;
\r
423 vPortTrace( "vPortGeneratePseudoInterrupt: Got interrupt mutex, about to signal interrupt event\r\n" );
\r
424 SetEvent( pvInterruptEvent );
\r
425 vPortTrace( "vPortGeneratePseudoInterrupt: About to release interrupt event mutex\r\n" );
\r
426 ReleaseMutex( pvInterruptEventMutex );
\r
427 vPortTrace( "vPortGeneratePseudoInterrupt: Interrupt event mutex released, going to wait for interrupt ack\r\n" );
\r
429 WaitForSingleObject( pvInterruptAcknowledgeEvent, INFINITE );
\r
430 vPortTrace( "vPortGeneratePseudoInterrupt: Interrupt acknowledged, leaving vPortGeneratePseudoInterrupt()\r\n" );
\r
434 /*-----------------------------------------------------------*/
\r
436 void vPortSetInterruptHandler( unsigned long ulInterruptNumber, void (*pvHandler)( void ) )
\r
438 if( ulInterruptNumber < portMAX_INTERRUPTS )
\r
440 if( pvInterruptEventMutex != NULL )
\r
442 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
\r
443 vIsrHandler[ ulInterruptNumber ] = pvHandler;
\r
444 ReleaseMutex( pvInterruptEventMutex );
\r
448 vIsrHandler[ ulInterruptNumber ] = pvHandler;
\r
452 /*-----------------------------------------------------------*/
\r
454 void vPortEnterCritical( void )
\r
456 ulCriticalNesting++;
\r
458 /*-----------------------------------------------------------*/
\r
460 void vPortExitCritical( void )
\r
462 xThreadState *pxThreadState;
\r
464 if( ulCriticalNesting > portNO_CRITICAL_NESTING )
\r
466 ulCriticalNesting--;
\r
468 if( ulCriticalNesting == 0 )
\r
470 /* Were any interrupts set to pending while interrupts were
\r
471 (pseudo) disabled? */
\r
472 if( ulPendingInterrupts != 0UL )
\r
474 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
\r
475 vPortTrace( "vPortExitCritical: Setting interrupt event\r\n" );
\r
476 SetEvent( pvInterruptEvent );
\r
478 /* The event handler needs to know to signal the interrupt acknowledge event
\r
479 the next time this task runs. */
\r
480 pxThreadState = ( xThreadState * ) *( ( unsigned long * ) pxCurrentTCB );
\r
481 pxThreadState->lWaitingInterruptAck = pdTRUE;
\r
483 ReleaseMutex( pvInterruptEventMutex );
\r
485 vPortTrace( "vPortExitCritical: Waiting interrupt ack\r\n" );
\r
486 WaitForSingleObject( pvInterruptAcknowledgeEvent, INFINITE );
\r
487 vPortTrace( "vPortExitCritical: Interrupt acknowledged, leaving critical section code\r\n" );
\r