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 pseudo 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 *pvMainThreadAndInterruptHandler;
\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 long 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 iteration of this loop rather than being set
\r
146 to function periodically - 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 pseudo 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 pseudo 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 pseudo
\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
426 /* The interrupt ack event should not be signaled yet - if it is then there
\r
427 is an error in the logical simulation. */
\r
428 if( WaitForSingleObject( pvInterruptAcknowledgeEvent, 0 ) != WAIT_TIMEOUT )
\r
430 /* This line is for a break point only. */
\r
434 vPortTrace( "vPortGeneratePseudoInterrupt: About to release interrupt event mutex\r\n" );
\r
435 ReleaseMutex( pvInterruptEventMutex );
\r
436 vPortTrace( "vPortGeneratePseudoInterrupt: Interrupt event mutex released, going to wait for interrupt ack\r\n" );
\r
438 WaitForSingleObject( pvInterruptAcknowledgeEvent, INFINITE );
\r
439 vPortTrace( "vPortGeneratePseudoInterrupt: Interrupt acknowledged, leaving vPortGeneratePseudoInterrupt()\r\n" );
\r
443 /*-----------------------------------------------------------*/
\r
445 void vPortSetInterruptHandler( unsigned long ulInterruptNumber, void (*pvHandler)( void ) )
\r
447 if( ulInterruptNumber < portMAX_INTERRUPTS )
\r
449 if( pvInterruptEventMutex != NULL )
\r
451 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
\r
452 vIsrHandler[ ulInterruptNumber ] = pvHandler;
\r
453 ReleaseMutex( pvInterruptEventMutex );
\r
457 vIsrHandler[ ulInterruptNumber ] = pvHandler;
\r
461 /*-----------------------------------------------------------*/
\r
463 void vPortEnterCritical( void )
\r
465 ulCriticalNesting++;
\r
467 /*-----------------------------------------------------------*/
\r
469 void vPortExitCritical( void )
\r
471 xThreadState *pxThreadState;
\r
473 if( ulCriticalNesting > portNO_CRITICAL_NESTING )
\r
475 ulCriticalNesting--;
\r
477 if( ulCriticalNesting == 0 )
\r
479 /* Were any interrupts set to pending while interrupts were
\r
480 (pseudo) disabled? */
\r
481 if( ulPendingInterrupts != 0UL )
\r
483 WaitForSingleObject( pvInterruptEventMutex, INFINITE );
\r
484 vPortTrace( "vPortExitCritical: Setting interrupt event\r\n" );
\r
485 SetEvent( pvInterruptEvent );
\r
487 /* The interrupt ack event should not be signaled yet - if it is then
\r
488 there is an error in the logical simulation. */
\r
489 if( WaitForSingleObject( pvInterruptAcknowledgeEvent, 0 ) != WAIT_TIMEOUT )
\r
491 /* This line is for a break point only. */
\r
495 /* The event handler needs to know to signal the interrupt acknowledge
\r
496 event the next time this task runs. */
\r
497 pxThreadState = ( xThreadState * ) *( ( unsigned long * ) pxCurrentTCB );
\r
498 pxThreadState->lWaitingInterruptAck = pdTRUE;
\r
500 ReleaseMutex( pvInterruptEventMutex );
\r
502 vPortTrace( "vPortExitCritical: Waiting interrupt ack\r\n" );
\r
503 WaitForSingleObject( pvInterruptAcknowledgeEvent, INFINITE );
\r
504 vPortTrace( "vPortExitCritical: Interrupt acknowledged, leaving critical section code\r\n" );
\r