/* | |
FreeRTOS V7.5.3 - Copyright (C) 2013 Real Time Engineers Ltd. | |
All rights reserved | |
VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. | |
*************************************************************************** | |
* * | |
* FreeRTOS provides completely free yet professionally developed, * | |
* robust, strictly quality controlled, supported, and cross * | |
* platform software that has become a de facto standard. * | |
* * | |
* Help yourself get started quickly and support the FreeRTOS * | |
* project by purchasing a FreeRTOS tutorial book, reference * | |
* manual, or both from: http://www.FreeRTOS.org/Documentation * | |
* * | |
* Thank you! * | |
* * | |
*************************************************************************** | |
This file is part of the FreeRTOS distribution. | |
FreeRTOS is free software; you can redistribute it and/or modify it under | |
the terms of the GNU General Public License (version 2) as published by the | |
Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception. | |
>>! NOTE: The modification to the GPL is included to allow you to distribute | |
>>! a combined work that includes FreeRTOS without being obliged to provide | |
>>! the source code for proprietary components outside of the FreeRTOS | |
>>! kernel. | |
FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY | |
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
FOR A PARTICULAR PURPOSE. Full license text is available from the following | |
link: http://www.freertos.org/a00114.html | |
1 tab == 4 spaces! | |
*************************************************************************** | |
* * | |
* Having a problem? Start by reading the FAQ "My application does * | |
* not run, what could be wrong?" * | |
* * | |
* http://www.FreeRTOS.org/FAQHelp.html * | |
* * | |
*************************************************************************** | |
http://www.FreeRTOS.org - Documentation, books, training, latest versions, | |
license and Real Time Engineers Ltd. contact details. | |
http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, | |
including FreeRTOS+Trace - an indispensable productivity tool, a DOS | |
compatible FAT file system, and our tiny thread aware UDP/IP stack. | |
http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High | |
Integrity Systems to sell under the OpenRTOS brand. Low cost OpenRTOS | |
licenses offer ticketed support, indemnification and middleware. | |
http://www.SafeRTOS.com - High Integrity Systems also provide a safety | |
engineered and independently SIL3 certified version for use in safety and | |
mission critical applications that require provable dependability. | |
1 tab == 4 spaces! | |
*/ | |
/* Standard includes. */ | |
#include <stdlib.h> | |
/* IAR includes. */ | |
#include <intrinsics.h> | |
/* Scheduler includes. */ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#ifndef configINTERRUPT_CONTROLLER_BASE_ADDRESS | |
#error configINTERRUPT_CONTROLLER_BASE_ADDRESS must be defined. See http://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html | |
#endif | |
#ifndef configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET | |
#error configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET must be defined. See http://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html | |
#endif | |
#ifndef configUNIQUE_INTERRUPT_PRIORITIES | |
#error configUNIQUE_INTERRUPT_PRIORITIES must be defined. See http://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html | |
#endif | |
#ifndef configSETUP_TICK_INTERRUPT | |
#error configSETUP_TICK_INTERRUPT() must be defined. See http://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html | |
#endif /* configSETUP_TICK_INTERRUPT */ | |
#ifndef configMAX_API_CALL_INTERRUPT_PRIORITY | |
#error configMAX_API_CALL_INTERRUPT_PRIORITY must be defined. See http://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html | |
#endif | |
#if configMAX_API_CALL_INTERRUPT_PRIORITY == 0 | |
#error configMAX_API_CALL_INTERRUPT_PRIORITY must not be set to 0 | |
#endif | |
#if configMAX_API_CALL_INTERRUPT_PRIORITY > configUNIQUE_INTERRUPT_PRIORITIES | |
#error configMAX_API_CALL_INTERRUPT_PRIORITY must be less than or equal to configUNIQUE_INTERRUPT_PRIORITIES as the lower the numeric priority value the higher the logical interrupt priority | |
#endif | |
#if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 | |
/* Check the configuration. */ | |
#if( configMAX_PRIORITIES > 32 ) | |
#error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice. | |
#endif | |
#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ | |
/* In case security extensions are implemented. */ | |
#if configMAX_API_CALL_INTERRUPT_PRIORITY <= ( configUNIQUE_INTERRUPT_PRIORITIES / 2 ) | |
#error configMAX_API_CALL_INTERRUPT_PRIORITY must be greater than ( configUNIQUE_INTERRUPT_PRIORITIES / 2 ) | |
#endif | |
/* A critical section is exited when the critical section nesting count reaches | |
this value. */ | |
#define portNO_CRITICAL_NESTING ( ( unsigned long ) 0 ) | |
/* In all GICs 255 can be written to the priority mask register to unmask all | |
(but the lowest) interrupt priority. */ | |
#define portUNMASK_VALUE ( 0xFF ) | |
/* Tasks are not created with a floating point context, but can be given a | |
floating point context after they have been created. A variable is stored as | |
part of the tasks context that holds portNO_FLOATING_POINT_CONTEXT if the task | |
does not have an FPU context, or any other value if the task does have an FPU | |
context. */ | |
#define portNO_FLOATING_POINT_CONTEXT ( ( portSTACK_TYPE ) 0 ) | |
/* Constants required to setup the initial task context. */ | |
#define portINITIAL_SPSR ( ( portSTACK_TYPE ) 0x1f ) /* System mode, ARM mode, interrupts enabled. */ | |
#define portTHUMB_MODE_BIT ( ( portSTACK_TYPE ) 0x20 ) | |
#define portINTERRUPT_ENABLE_BIT ( 0x80UL ) | |
#define portTHUMB_MODE_ADDRESS ( 0x01UL ) | |
/* Used by portASSERT_IF_INTERRUPT_PRIORITY_INVALID() when ensuring the binary | |
point is zero. */ | |
#define portBINARY_POINT_BITS ( ( unsigned char ) 0x03 ) | |
/* Masks all bits in the APSR other than the mode bits. */ | |
#define portAPSR_MODE_BITS_MASK ( 0x1F ) | |
/* The value of the mode bits in the APSR when the CPU is executing in user | |
mode. */ | |
#define portAPSR_USER_MODE ( 0x10 ) | |
/* Macro to unmask all interrupt priorities. */ | |
#define portCLEAR_INTERRUPT_MASK() \ | |
{ \ | |
__disable_irq(); \ | |
portICCPMR_PRIORITY_MASK_REGISTER = portUNMASK_VALUE; \ | |
__asm( "DSB \n" \ | |
"ISB \n" ); \ | |
__enable_irq(); \ | |
} | |
/*-----------------------------------------------------------*/ | |
/* | |
* Starts the first task executing. This function is necessarily written in | |
* assembly code so is implemented in portASM.s. | |
*/ | |
extern void vPortRestoreTaskContext( void ); | |
/*-----------------------------------------------------------*/ | |
/* A variable is used to keep track of the critical section nesting. This | |
variable has to be stored as part of the task context and must be initialised to | |
a non zero value to ensure interrupts don't inadvertently become unmasked before | |
the scheduler starts. As it is stored as part of the task context it will | |
automatically be set to 0 when the first task is started. */ | |
volatile unsigned long ulCriticalNesting = 9999UL; | |
/* Saved as part of the task context. If ulPortTaskHasFPUContext is non-zero then | |
a floating point context must be saved and restored for the task. */ | |
unsigned long ulPortTaskHasFPUContext = pdFALSE; | |
/* Set to 1 to pend a context switch from an ISR. */ | |
unsigned long ulPortYieldRequired = pdFALSE; | |
/* Counts the interrupt nesting depth. A context switch is only performed if | |
if the nesting depth is 0. */ | |
unsigned long ulPortInterruptNesting = 0UL; | |
/*-----------------------------------------------------------*/ | |
/* | |
* See header file for description. | |
*/ | |
portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters ) | |
{ | |
/* Setup the initial stack of the task. The stack is set exactly as | |
expected by the portRESTORE_CONTEXT() macro. | |
The fist real value on the stack is the status register, which is set for | |
system mode, with interrupts enabled. A few NULLs are added first to ensure | |
GDB does not try decoding a non-existent return address. */ | |
*pxTopOfStack = NULL; | |
pxTopOfStack--; | |
*pxTopOfStack = NULL; | |
pxTopOfStack--; | |
*pxTopOfStack = NULL; | |
pxTopOfStack--; | |
*pxTopOfStack = ( portSTACK_TYPE ) portINITIAL_SPSR; | |
if( ( ( unsigned long ) pxCode & portTHUMB_MODE_ADDRESS ) != 0x00UL ) | |
{ | |
/* The task will start in THUMB mode. */ | |
*pxTopOfStack |= portTHUMB_MODE_BIT; | |
} | |
pxTopOfStack--; | |
/* Next the return address, which in this case is the start of the task. */ | |
*pxTopOfStack = ( portSTACK_TYPE ) pxCode; | |
pxTopOfStack--; | |
/* Next all the registers other than the stack pointer. */ | |
*pxTopOfStack = ( portSTACK_TYPE ) 0x00000000; /* R14 */ | |
pxTopOfStack--; | |
*pxTopOfStack = ( portSTACK_TYPE ) 0x12121212; /* R12 */ | |
pxTopOfStack--; | |
*pxTopOfStack = ( portSTACK_TYPE ) 0x11111111; /* R11 */ | |
pxTopOfStack--; | |
*pxTopOfStack = ( portSTACK_TYPE ) 0x10101010; /* R10 */ | |
pxTopOfStack--; | |
*pxTopOfStack = ( portSTACK_TYPE ) 0x09090909; /* R9 */ | |
pxTopOfStack--; | |
*pxTopOfStack = ( portSTACK_TYPE ) 0x08080808; /* R8 */ | |
pxTopOfStack--; | |
*pxTopOfStack = ( portSTACK_TYPE ) 0x07070707; /* R7 */ | |
pxTopOfStack--; | |
*pxTopOfStack = ( portSTACK_TYPE ) 0x06060606; /* R6 */ | |
pxTopOfStack--; | |
*pxTopOfStack = ( portSTACK_TYPE ) 0x05050505; /* R5 */ | |
pxTopOfStack--; | |
*pxTopOfStack = ( portSTACK_TYPE ) 0x04040404; /* R4 */ | |
pxTopOfStack--; | |
*pxTopOfStack = ( portSTACK_TYPE ) 0x03030303; /* R3 */ | |
pxTopOfStack--; | |
*pxTopOfStack = ( portSTACK_TYPE ) 0x02020202; /* R2 */ | |
pxTopOfStack--; | |
*pxTopOfStack = ( portSTACK_TYPE ) 0x01010101; /* R1 */ | |
pxTopOfStack--; | |
*pxTopOfStack = ( portSTACK_TYPE ) pvParameters; /* R0 */ | |
pxTopOfStack--; | |
/* The task will start with a critical nesting count of 0 as interrupts are | |
enabled. */ | |
*pxTopOfStack = portNO_CRITICAL_NESTING; | |
pxTopOfStack--; | |
/* The task will start without a floating point context. A task that uses | |
the floating point hardware must call vPortTaskUsesFPU() before executing | |
any floating point instructions. */ | |
*pxTopOfStack = portNO_FLOATING_POINT_CONTEXT; | |
return pxTopOfStack; | |
} | |
/*-----------------------------------------------------------*/ | |
portBASE_TYPE xPortStartScheduler( void ) | |
{ | |
unsigned long ulAPSR; | |
/* Only continue if the CPU is not in User mode. The CPU must be in a | |
Privileged mode for the scheduler to start. */ | |
__asm volatile ( "MRS %0, APSR" : "=r" ( ulAPSR ) ); | |
ulAPSR &= portAPSR_MODE_BITS_MASK; | |
configASSERT( ulAPSR != portAPSR_USER_MODE ); | |
if( ulAPSR != portAPSR_USER_MODE ) | |
{ | |
/* Only continue if the binary point value is set to its lowest possible | |
setting. See the comments in vPortValidateInterruptPriority() below for | |
more information. */ | |
configASSERT( ( portICCBPR_BINARY_POINT_REGISTER & portBINARY_POINT_BITS ) <= portMAX_BINARY_POINT_VALUE ); | |
if( ( portICCBPR_BINARY_POINT_REGISTER & portBINARY_POINT_BITS ) <= portMAX_BINARY_POINT_VALUE ) | |
{ | |
/* Start the timer that generates the tick ISR. */ | |
configSETUP_TICK_INTERRUPT(); | |
__enable_irq(); | |
vPortRestoreTaskContext(); | |
} | |
} | |
/* Will only get here if xTaskStartScheduler() was called with the CPU in | |
a non-privileged mode or the binary point register was not set to its lowest | |
possible value. */ | |
return 0; | |
} | |
/*-----------------------------------------------------------*/ | |
void vPortEndScheduler( void ) | |
{ | |
/* It is unlikely that the ARM port will require this function as there | |
is nothing to return to. */ | |
} | |
/*-----------------------------------------------------------*/ | |
void vPortEnterCritical( void ) | |
{ | |
/* Disable interrupts as per portDISABLE_INTERRUPTS(); */ | |
ulPortSetInterruptMask(); | |
/* Now interrupts are disabled ulCriticalNesting can be accessed | |
directly. Increment ulCriticalNesting to keep a count of how many times | |
portENTER_CRITICAL() has been called. */ | |
ulCriticalNesting++; | |
} | |
/*-----------------------------------------------------------*/ | |
void vPortExitCritical( void ) | |
{ | |
if( ulCriticalNesting > portNO_CRITICAL_NESTING ) | |
{ | |
/* Decrement the nesting count as the critical section is being | |
exited. */ | |
ulCriticalNesting--; | |
/* If the nesting level has reached zero then all interrupt | |
priorities must be re-enabled. */ | |
if( ulCriticalNesting == portNO_CRITICAL_NESTING ) | |
{ | |
/* Critical nesting has reached zero so all interrupt priorities | |
should be unmasked. */ | |
portCLEAR_INTERRUPT_MASK(); | |
} | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
void FreeRTOS_Tick_Handler( void ) | |
{ | |
/* Set interrupt mask before altering scheduler structures. The tick | |
handler runs at the lowest priority, so interrupts cannot already be masked, | |
so there is no need to save and restore the current mask value. */ | |
__disable_irq(); | |
portICCPMR_PRIORITY_MASK_REGISTER = ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ); | |
__asm( "DSB \n" | |
"ISB \n" ); | |
__enable_irq(); | |
/* Increment the RTOS tick. */ | |
if( xTaskIncrementTick() != pdFALSE ) | |
{ | |
ulPortYieldRequired = pdTRUE; | |
} | |
/* Ensure all interrupt priorities are active again. */ | |
portCLEAR_INTERRUPT_MASK(); | |
} | |
/*-----------------------------------------------------------*/ | |
void vPortTaskUsesFPU( void ) | |
{ | |
unsigned long ulInitialFPSCR = 0; | |
/* A task is registering the fact that it needs an FPU context. Set the | |
FPU flag (which is saved as part of the task context). */ | |
ulPortTaskHasFPUContext = pdTRUE; | |
/* Initialise the floating point status register. */ | |
__asm( "FMXR FPSCR, %0" :: "r" (ulInitialFPSCR) ); | |
} | |
/*-----------------------------------------------------------*/ | |
void vPortClearInterruptMask( unsigned long ulNewMaskValue ) | |
{ | |
if( ulNewMaskValue == pdFALSE ) | |
{ | |
portCLEAR_INTERRUPT_MASK(); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
unsigned long ulPortSetInterruptMask( void ) | |
{ | |
unsigned long ulReturn; | |
__disable_irq(); | |
if( portICCPMR_PRIORITY_MASK_REGISTER == ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) ) | |
{ | |
/* Interrupts were already masked. */ | |
ulReturn = pdTRUE; | |
} | |
else | |
{ | |
ulReturn = pdFALSE; | |
portICCPMR_PRIORITY_MASK_REGISTER = ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ); | |
__asm( "DSB \n" | |
"ISB \n" ); | |
} | |
__enable_irq(); | |
return ulReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
#if( configASSERT_DEFINED == 1 ) | |
void vPortValidateInterruptPriority( void ) | |
{ | |
/* The following assertion will fail if a service routine (ISR) for | |
an interrupt that has been assigned a priority above | |
configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API | |
function. ISR safe FreeRTOS API functions must *only* be called | |
from interrupts that have been assigned a priority at or below | |
configMAX_SYSCALL_INTERRUPT_PRIORITY. | |
Numerically low interrupt priority numbers represent logically high | |
interrupt priorities, therefore the priority of the interrupt must | |
be set to a value equal to or numerically *higher* than | |
configMAX_SYSCALL_INTERRUPT_PRIORITY. | |
FreeRTOS maintains separate thread and ISR API functions to ensure | |
interrupt entry is as fast and simple as possible. | |
The following links provide detailed information: | |
http://www.freertos.org/RTOS-Cortex-M3-M4.html | |
http://www.freertos.org/FAQHelp.html */ | |
configASSERT( portICCRPR_RUNNING_PRIORITY_REGISTER >= ( configMAX_API_CALL_INTERRUPT_PRIORITY << portPRIORITY_SHIFT ) ); | |
/* Priority grouping: The interrupt controller (GIC) allows the bits | |
that define each interrupt's priority to be split between bits that | |
define the interrupt's pre-emption priority bits and bits that define | |
the interrupt's sub-priority. For simplicity all bits must be defined | |
to be pre-emption priority bits. The following assertion will fail if | |
this is not the case (if some bits represent a sub-priority). | |
The priority grouping is configured by the GIC's binary point register | |
(ICCBPR). Writting 0 to ICCBPR will ensure it is set to its lowest | |
possible value (which may be above 0). */ | |
configASSERT( ( portICCBPR_BINARY_POINT_REGISTER & portBINARY_POINT_BITS ) <= portMAX_BINARY_POINT_VALUE ); | |
} | |
#endif /* configASSERT_DEFINED */ | |