2 * FreeRTOS Kernel <DEVELOPMENT BRANCH>
3 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 * SPDX-License-Identifier: MIT
7 * Permission is hereby granted, free of charge, to any person obtaining a copy of
8 * this software and associated documentation files (the "Software"), to deal in
9 * the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 * the Software, and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 * https://www.FreeRTOS.org
25 * https://github.com/FreeRTOS
29 /*-----------------------------------------------------------
30 * Implementation of functions defined in portable.h for the PIC32MX port.
31 *----------------------------------------------------------*/
34 #error This port is designed to work with XC32. Please update your C compiler version.
37 /* Scheduler include files. */
41 /* Hardware specifics. */
42 #define portTIMER_PRESCALE 8
43 #define portPRESCALE_BITS 1
45 /* Bits within various registers. */
46 #define portIE_BIT ( 0x00000001 )
47 #define portEXL_BIT ( 0x00000002 )
49 /* Bits within the CAUSE register. */
50 #define portCORE_SW_0 ( 0x00000100 )
51 #define portCORE_SW_1 ( 0x00000200 )
53 /* The EXL bit is set to ensure interrupts do not occur while the context of
54 * the first task is being restored. */
55 #define portINITIAL_SR ( portIE_BIT | portEXL_BIT )
58 * By default port.c generates its tick interrupt from TIMER1. The user can
59 * override this behaviour by:
60 * 1: Providing their own implementation of vApplicationSetupTickTimerInterrupt(),
61 * which is the function that configures the timer. The function is defined
62 * as a weak symbol in this file so if the same function name is used in the
63 * application code then the version in the application code will be linked
64 * into the application in preference to the version defined in this file.
65 * 2: Define configTICK_INTERRUPT_VECTOR to the vector number of the timer used
66 * to generate the tick interrupt. For example, when timer 1 is used then
67 * configTICK_INTERRUPT_VECTOR is set to _TIMER_1_VECTOR.
68 * configTICK_INTERRUPT_VECTOR should be defined in FreeRTOSConfig.h.
69 * 3: Define configCLEAR_TICK_TIMER_INTERRUPT() to clear the interrupt in the
70 * timer used to generate the tick interrupt. For example, when timer 1 is
71 * used configCLEAR_TICK_TIMER_INTERRUPT() is defined to
72 * IFS0CLR = _IFS0_T1IF_MASK.
74 #ifndef configTICK_INTERRUPT_VECTOR
75 #define configTICK_INTERRUPT_VECTOR _TIMER_1_VECTOR
76 #define configCLEAR_TICK_TIMER_INTERRUPT() IFS0CLR = _IFS0_T1IF_MASK
78 #ifndef configCLEAR_TICK_TIMER_INTERRUPT
79 #error If configTICK_INTERRUPT_VECTOR is defined in application code then configCLEAR_TICK_TIMER_INTERRUPT must also be defined in application code.
83 /* Let the user override the pre-loading of the initial RA with the address of
84 * prvTaskExitError() in case it messes up unwinding of the stack in the
85 * debugger - in which case configTASK_RETURN_ADDRESS can be defined as 0 (NULL). */
86 #ifdef configTASK_RETURN_ADDRESS
87 #define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS
89 #define portTASK_RETURN_ADDRESS prvTaskExitError
92 /* Set configCHECK_FOR_STACK_OVERFLOW to 3 to add ISR stack checking to task
93 * stack checking. A problem in the ISR stack will trigger an assert, not call the
94 * stack overflow hook function (because the stack overflow hook is specific to a
95 * task stack, not the ISR stack). */
96 #if ( configCHECK_FOR_STACK_OVERFLOW > 2 )
98 /* Don't use 0xa5 as the stack fill bytes as that is used by the kernel for
99 * the task stacks, and so will legitimately appear in many positions within
101 #define portISR_STACK_FILL_BYTE 0xee
103 static const uint8_t ucExpectedStackBytes[] =
105 portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
106 portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
107 portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
108 portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, \
109 portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE
112 #define portCHECK_ISR_STACK() configASSERT( ( memcmp( ( void * ) xISRStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) == 0 ) )
113 #else /* if ( configCHECK_FOR_STACK_OVERFLOW > 2 ) */
114 /* Define the function away. */
115 #define portCHECK_ISR_STACK()
116 #endif /* configCHECK_FOR_STACK_OVERFLOW > 2 */
118 /*-----------------------------------------------------------*/
122 * Place the prototype here to ensure the interrupt vector is correctly installed.
123 * Note that because the interrupt is written in assembly, the IPL setting in the
124 * following line of code has no effect. The interrupt priority is set by the
125 * call to ConfigIntTimer1() in vApplicationSetupTickTimerInterrupt().
127 extern void __attribute__( ( interrupt( IPL1AUTO ), vector( configTICK_INTERRUPT_VECTOR ) ) ) vPortTickInterruptHandler( void );
130 * The software interrupt handler that performs the yield. Note that, because
131 * the interrupt is written in assembly, the IPL setting in the following line of
132 * code has no effect. The interrupt priority is set by the call to
133 * mConfigIntCoreSW0() in xPortStartScheduler().
135 void __attribute__( ( interrupt( IPL1AUTO ), vector( _CORE_SOFTWARE_0_VECTOR ) ) ) vPortYieldISR( void );
138 * Used to catch tasks that attempt to return from their implementing function.
140 static void prvTaskExitError( void );
142 /*-----------------------------------------------------------*/
144 /* Records the interrupt nesting depth. This is initialised to one as it is
145 * decremented to 0 when the first task starts. */
146 volatile UBaseType_t uxInterruptNesting = 0x01;
148 /* Stores the task stack pointer when a switch is made to use the system stack. */
149 UBaseType_t uxSavedTaskStackPointer = 0;
151 /* The stack used by interrupt service routines that cause a context switch. */
152 __attribute__( ( aligned( 8 ) ) ) StackType_t xISRStack[ configISR_STACK_SIZE ] = { 0 };
154 /* The top of stack value ensures there is enough space to store 6 registers on
155 * the callers stack, as some functions seem to want to do this. */
156 const StackType_t * const xISRStackTop = &( xISRStack[ ( configISR_STACK_SIZE & ~portBYTE_ALIGNMENT_MASK ) - 8 ] );
158 /*-----------------------------------------------------------*/
161 * See header file for description.
163 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
164 TaskFunction_t pxCode,
165 void * pvParameters )
167 /* Ensure 8 byte alignment is maintained when the context is popped from
168 * stack. The size of the context is 33 words (132 bytes). */
172 *pxTopOfStack = ( StackType_t ) 0xDEADBEEF;
175 *pxTopOfStack = ( StackType_t ) 0x12345678; /* Word to which the stack pointer will be left pointing after context restore. */
178 *pxTopOfStack = ( StackType_t ) _CP0_GET_CAUSE();
181 *pxTopOfStack = ( StackType_t ) portINITIAL_SR; /* CP0_STATUS */
184 *pxTopOfStack = ( StackType_t ) pxCode; /* CP0_EPC */
187 *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* ra */
190 *pxTopOfStack = ( StackType_t ) pvParameters; /* Parameters to pass in. */
195 /*-----------------------------------------------------------*/
197 static void prvTaskExitError( void )
199 /* A function that implements a task must not exit or attempt to return to
200 * its caller as there is nothing to return to. If a task wants to exit it
201 * should instead call vTaskDelete( NULL ).
203 * Artificially force an assert() to be triggered if configASSERT() is
204 * defined, then stop here so application writers can catch the error. */
205 configASSERT( uxSavedTaskStackPointer == 0UL );
206 portDISABLE_INTERRUPTS();
212 /*-----------------------------------------------------------*/
215 * Setup a timer for a regular tick. This function uses peripheral timer 1.
216 * The function is declared weak so an application writer can use a different
217 * timer by redefining this implementation. If a different timer is used then
218 * configTICK_INTERRUPT_VECTOR must also be defined in FreeRTOSConfig.h to
219 * ensure the RTOS provided tick interrupt handler is installed on the correct
220 * vector number. When Timer 1 is used the vector number is defined as
223 __attribute__( ( weak ) ) void vApplicationSetupTickTimerInterrupt( void )
225 const uint32_t ulCompareMatch = ( ( configPERIPHERAL_CLOCK_HZ / portTIMER_PRESCALE ) / configTICK_RATE_HZ ) - 1;
228 T1CONbits.TCKPS = portPRESCALE_BITS;
229 PR1 = ulCompareMatch;
230 IPC1bits.T1IP = configKERNEL_INTERRUPT_PRIORITY;
232 /* Clear the interrupt as a starting condition. */
235 /* Enable the interrupt. */
238 /* Start the timer. */
241 /*-----------------------------------------------------------*/
243 void vPortEndScheduler( void )
245 /* Not implemented in ports where there is nothing to return to.
246 * Artificially force an assert. */
247 configASSERT( uxInterruptNesting == 1000UL );
249 /*-----------------------------------------------------------*/
251 BaseType_t xPortStartScheduler( void )
253 extern void vPortStartFirstTask( void );
254 extern void * pxCurrentTCB;
256 #if ( configCHECK_FOR_STACK_OVERFLOW > 2 )
258 /* Fill the ISR stack to make it easy to asses how much is being used. */
259 memset( ( void * ) xISRStack, portISR_STACK_FILL_BYTE, sizeof( xISRStack ) );
261 #endif /* configCHECK_FOR_STACK_OVERFLOW > 2 */
263 /* Clear the software interrupt flag. */
264 IFS0CLR = _IFS0_CS0IF_MASK;
266 /* Set software timer priority. */
267 IPC0CLR = _IPC0_CS0IP_MASK;
268 IPC0SET = ( configKERNEL_INTERRUPT_PRIORITY << _IPC0_CS0IP_POSITION );
270 /* Enable software interrupt. */
271 IEC0CLR = _IEC0_CS0IE_MASK;
272 IEC0SET = 1 << _IEC0_CS0IE_POSITION;
274 /* Setup the timer to generate the tick. Interrupts will have been
275 * disabled by the time we get here. */
276 vApplicationSetupTickTimerInterrupt();
278 /* Kick off the highest priority task that has been created so far.
279 * Its stack location is loaded into uxSavedTaskStackPointer. */
280 uxSavedTaskStackPointer = *( UBaseType_t * ) pxCurrentTCB;
281 vPortStartFirstTask();
283 /* Should never get here as the tasks will now be executing! Call the task
284 * exit error function to prevent compiler warnings about a static function
285 * not being called in the case that the application writer overrides this
286 * functionality by defining configTASK_RETURN_ADDRESS. */
291 /*-----------------------------------------------------------*/
293 void vPortIncrementTick( void )
295 UBaseType_t uxSavedStatus;
297 uxSavedStatus = uxPortSetInterruptMaskFromISR();
299 if( xTaskIncrementTick() != pdFALSE )
301 /* Pend a context switch. */
302 _CP0_BIS_CAUSE( portCORE_SW_0 );
305 vPortClearInterruptMaskFromISR( uxSavedStatus );
307 /* Look for the ISR stack getting near or past its limit. */
308 portCHECK_ISR_STACK();
310 /* Clear timer interrupt. */
311 configCLEAR_TICK_TIMER_INTERRUPT();
313 /*-----------------------------------------------------------*/
315 UBaseType_t uxPortSetInterruptMaskFromISR( void )
317 UBaseType_t uxSavedStatusRegister;
319 __builtin_disable_interrupts();
320 uxSavedStatusRegister = _CP0_GET_STATUS() | 0x01;
322 /* This clears the IPL bits, then sets them to
323 * configMAX_SYSCALL_INTERRUPT_PRIORITY. This function should not be called
324 * from an interrupt that has a priority above
325 * configMAX_SYSCALL_INTERRUPT_PRIORITY so, when used correctly, the action
326 * can only result in the IPL being unchanged or raised, and therefore never
328 _CP0_SET_STATUS( ( ( uxSavedStatusRegister & ( ~portALL_IPL_BITS ) ) ) | ( configMAX_SYSCALL_INTERRUPT_PRIORITY << portIPL_SHIFT ) );
330 return uxSavedStatusRegister;
332 /*-----------------------------------------------------------*/
334 void vPortClearInterruptMaskFromISR( UBaseType_t uxSavedStatusRegister )
336 _CP0_SET_STATUS( uxSavedStatusRegister );
338 /*-----------------------------------------------------------*/