]> begriffs open source - freertos/blob - portable/MPLAB/PIC32MX/port.c
CI-CD Updates (#768)
[freertos] / portable / MPLAB / PIC32MX / port.c
1 /*
2  * FreeRTOS Kernel <DEVELOPMENT BRANCH>
3  * Copyright (C) 2021 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
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:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
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.
23  *
24  * https://www.FreeRTOS.org
25  * https://github.com/FreeRTOS
26  *
27  */
28
29 /*-----------------------------------------------------------
30 * Implementation of functions defined in portable.h for the PIC32MX port.
31 *----------------------------------------------------------*/
32
33 #ifndef __XC
34     #error This port is designed to work with XC32.  Please update your C compiler version.
35 #endif
36
37 /* Scheduler include files. */
38 #include "FreeRTOS.h"
39 #include "task.h"
40
41 /* Hardware specifics. */
42 #define portTIMER_PRESCALE    8
43 #define portPRESCALE_BITS     1
44
45 /* Bits within various registers. */
46 #define portIE_BIT            ( 0x00000001 )
47 #define portEXL_BIT           ( 0x00000002 )
48
49 /* Bits within the CAUSE register. */
50 #define portCORE_SW_0         ( 0x00000100 )
51 #define portCORE_SW_1         ( 0x00000200 )
52
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 )
56
57 /*
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.
73  */
74 #ifndef configTICK_INTERRUPT_VECTOR
75     #define configTICK_INTERRUPT_VECTOR    _TIMER_1_VECTOR
76     #define configCLEAR_TICK_TIMER_INTERRUPT()    IFS0CLR = _IFS0_T1IF_MASK
77 #else
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.
80     #endif
81 #endif
82
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
88 #else
89     #define portTASK_RETURN_ADDRESS    prvTaskExitError
90 #endif
91
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 )
97
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
100  * the ISR stack. */
101     #define portISR_STACK_FILL_BYTE    0xee
102
103     static const uint8_t ucExpectedStackBytes[] =
104     {
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
110     }; \
111
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 */
117
118 /*-----------------------------------------------------------*/
119
120
121 /*
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().
126  */
127 extern void __attribute__( ( interrupt( IPL1AUTO ), vector( configTICK_INTERRUPT_VECTOR ) ) ) vPortTickInterruptHandler( void );
128
129 /*
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().
134  */
135 void __attribute__( ( interrupt( IPL1AUTO ), vector( _CORE_SOFTWARE_0_VECTOR ) ) ) vPortYieldISR( void );
136
137 /*
138  * Used to catch tasks that attempt to return from their implementing function.
139  */
140 static void prvTaskExitError( void );
141
142 /*-----------------------------------------------------------*/
143
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;
147
148 /* Stores the task stack pointer when a switch is made to use the system stack. */
149 UBaseType_t uxSavedTaskStackPointer = 0;
150
151 /* The stack used by interrupt service routines that cause a context switch. */
152 __attribute__( ( aligned( 8 ) ) ) StackType_t xISRStack[ configISR_STACK_SIZE ] = { 0 };
153
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 ] );
157
158 /*-----------------------------------------------------------*/
159
160 /*
161  * See header file for description.
162  */
163 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
164                                      TaskFunction_t pxCode,
165                                      void * pvParameters )
166 {
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). */
169     pxTopOfStack--;
170     pxTopOfStack--;
171
172     *pxTopOfStack = ( StackType_t ) 0xDEADBEEF;
173     pxTopOfStack--;
174
175     *pxTopOfStack = ( StackType_t ) 0x12345678; /* Word to which the stack pointer will be left pointing after context restore. */
176     pxTopOfStack--;
177
178     *pxTopOfStack = ( StackType_t ) _CP0_GET_CAUSE();
179     pxTopOfStack--;
180
181     *pxTopOfStack = ( StackType_t ) portINITIAL_SR; /* CP0_STATUS */
182     pxTopOfStack--;
183
184     *pxTopOfStack = ( StackType_t ) pxCode; /* CP0_EPC */
185     pxTopOfStack--;
186
187     *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* ra */
188     pxTopOfStack -= 15;
189
190     *pxTopOfStack = ( StackType_t ) pvParameters; /* Parameters to pass in. */
191     pxTopOfStack -= 15;
192
193     return pxTopOfStack;
194 }
195 /*-----------------------------------------------------------*/
196
197 static void prvTaskExitError( void )
198 {
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 ).
202      *
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();
207
208     for( ; ; )
209     {
210     }
211 }
212 /*-----------------------------------------------------------*/
213
214 /*
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
221  * _TIMER_1_VECTOR.
222  */
223 __attribute__( ( weak ) ) void vApplicationSetupTickTimerInterrupt( void )
224 {
225     const uint32_t ulCompareMatch = ( ( configPERIPHERAL_CLOCK_HZ / portTIMER_PRESCALE ) / configTICK_RATE_HZ ) - 1;
226
227     T1CON = 0x0000;
228     T1CONbits.TCKPS = portPRESCALE_BITS;
229     PR1 = ulCompareMatch;
230     IPC1bits.T1IP = configKERNEL_INTERRUPT_PRIORITY;
231
232     /* Clear the interrupt as a starting condition. */
233     IFS0bits.T1IF = 0;
234
235     /* Enable the interrupt. */
236     IEC0bits.T1IE = 1;
237
238     /* Start the timer. */
239     T1CONbits.TON = 1;
240 }
241 /*-----------------------------------------------------------*/
242
243 void vPortEndScheduler( void )
244 {
245     /* Not implemented in ports where there is nothing to return to.
246      * Artificially force an assert. */
247     configASSERT( uxInterruptNesting == 1000UL );
248 }
249 /*-----------------------------------------------------------*/
250
251 BaseType_t xPortStartScheduler( void )
252 {
253     extern void vPortStartFirstTask( void );
254     extern void * pxCurrentTCB;
255
256     #if ( configCHECK_FOR_STACK_OVERFLOW > 2 )
257     {
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 ) );
260     }
261     #endif /* configCHECK_FOR_STACK_OVERFLOW > 2 */
262
263     /* Clear the software interrupt flag. */
264     IFS0CLR = _IFS0_CS0IF_MASK;
265
266     /* Set software timer priority. */
267     IPC0CLR = _IPC0_CS0IP_MASK;
268     IPC0SET = ( configKERNEL_INTERRUPT_PRIORITY << _IPC0_CS0IP_POSITION );
269
270     /* Enable software interrupt. */
271     IEC0CLR = _IEC0_CS0IE_MASK;
272     IEC0SET = 1 << _IEC0_CS0IE_POSITION;
273
274     /* Setup the timer to generate the tick.  Interrupts will have been
275      * disabled by the time we get here. */
276     vApplicationSetupTickTimerInterrupt();
277
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();
282
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. */
287     prvTaskExitError();
288
289     return pdFALSE;
290 }
291 /*-----------------------------------------------------------*/
292
293 void vPortIncrementTick( void )
294 {
295     UBaseType_t uxSavedStatus;
296
297     uxSavedStatus = uxPortSetInterruptMaskFromISR();
298     {
299         if( xTaskIncrementTick() != pdFALSE )
300         {
301             /* Pend a context switch. */
302             _CP0_BIS_CAUSE( portCORE_SW_0 );
303         }
304     }
305     vPortClearInterruptMaskFromISR( uxSavedStatus );
306
307     /* Look for the ISR stack getting near or past its limit. */
308     portCHECK_ISR_STACK();
309
310     /* Clear timer interrupt. */
311     configCLEAR_TICK_TIMER_INTERRUPT();
312 }
313 /*-----------------------------------------------------------*/
314
315 UBaseType_t uxPortSetInterruptMaskFromISR( void )
316 {
317     UBaseType_t uxSavedStatusRegister;
318
319     __builtin_disable_interrupts();
320     uxSavedStatusRegister = _CP0_GET_STATUS() | 0x01;
321
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
327      * lowered. */
328     _CP0_SET_STATUS( ( ( uxSavedStatusRegister & ( ~portALL_IPL_BITS ) ) ) | ( configMAX_SYSCALL_INTERRUPT_PRIORITY << portIPL_SHIFT ) );
329
330     return uxSavedStatusRegister;
331 }
332 /*-----------------------------------------------------------*/
333
334 void vPortClearInterruptMaskFromISR( UBaseType_t uxSavedStatusRegister )
335 {
336     _CP0_SET_STATUS( uxSavedStatusRegister );
337 }
338 /*-----------------------------------------------------------*/