]> git.sur5r.net Git - freertos/blob - FreeRTOS/Source/portable/RVDS/ARM_CM4_MPU/port.c
Update to MIT licensed FreeRTOS V10.0.0 - see https://www.freertos.org/History.txt
[freertos] / FreeRTOS / Source / portable / RVDS / ARM_CM4_MPU / port.c
1 /*\r
2  * FreeRTOS Kernel V10.0.0\r
3  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software. If you wish to use our Amazon\r
14  * FreeRTOS name, please do so in a fair use way that does not cause confusion.\r
15  *\r
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
18  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
19  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
22  *\r
23  * http://www.FreeRTOS.org\r
24  * http://aws.amazon.com/freertos\r
25  *\r
26  * 1 tab == 4 spaces!\r
27  */\r
28 \r
29 /*-----------------------------------------------------------\r
30  * Implementation of functions defined in portable.h for the ARM CM3 port.\r
31  *----------------------------------------------------------*/\r
32 \r
33 /* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining\r
34 all the API functions to use the MPU wrappers.  That should only be done when\r
35 task.h is included from an application file. */\r
36 #define MPU_WRAPPERS_INCLUDED_FROM_API_FILE\r
37 \r
38 /* Scheduler includes. */\r
39 #include "FreeRTOS.h"\r
40 #include "task.h"\r
41 \r
42 #ifndef __TARGET_FPU_VFP\r
43         #error This port can only be used when the project options are configured to enable hardware floating point support.\r
44 #endif\r
45 \r
46 #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE\r
47 \r
48 /* Constants required to access and manipulate the NVIC. */\r
49 #define portNVIC_SYSTICK_CTRL_REG                               ( * ( ( volatile uint32_t * ) 0xe000e010 ) )\r
50 #define portNVIC_SYSTICK_LOAD_REG                               ( * ( ( volatile uint32_t * ) 0xe000e014 ) )\r
51 #define portNVIC_SYSTICK_CURRENT_VALUE_REG              ( * ( ( volatile uint32_t * ) 0xe000e018 ) )\r
52 #define portNVIC_SYSPRI2_REG                                    ( *     ( ( volatile uint32_t * ) 0xe000ed20 ) )\r
53 #define portNVIC_SYSPRI1_REG                                    ( * ( ( volatile uint32_t * ) 0xe000ed1c ) )\r
54 #define portNVIC_SYS_CTRL_STATE_REG                             ( * ( ( volatile uint32_t * ) 0xe000ed24 ) )\r
55 #define portNVIC_MEM_FAULT_ENABLE                               ( 1UL << 16UL )\r
56 \r
57 /* Constants required to access and manipulate the MPU. */\r
58 #define portMPU_TYPE_REG                                                ( * ( ( volatile uint32_t * ) 0xe000ed90 ) )\r
59 #define portMPU_REGION_BASE_ADDRESS_REG                 ( * ( ( volatile uint32_t * ) 0xe000ed9C ) )\r
60 #define portMPU_REGION_ATTRIBUTE_REG                    ( * ( ( volatile uint32_t * ) 0xe000edA0 ) )\r
61 #define portMPU_CTRL_REG                                                ( * ( ( volatile uint32_t * ) 0xe000ed94 ) )\r
62 #define portEXPECTED_MPU_TYPE_VALUE                             ( 8UL << 8UL ) /* 8 regions, unified. */\r
63 #define portMPU_ENABLE                                                  ( 0x01UL )\r
64 #define portMPU_BACKGROUND_ENABLE                               ( 1UL << 2UL )\r
65 #define portPRIVILEGED_EXECUTION_START_ADDRESS  ( 0UL )\r
66 #define portMPU_REGION_VALID                                    ( 0x10UL )\r
67 #define portMPU_REGION_ENABLE                                   ( 0x01UL )\r
68 #define portPERIPHERALS_START_ADDRESS                   0x40000000UL\r
69 #define portPERIPHERALS_END_ADDRESS                             0x5FFFFFFFUL\r
70 \r
71 /* Constants required to access and manipulate the SysTick. */\r
72 #define portNVIC_SYSTICK_CLK                                    ( 0x00000004UL )\r
73 #define portNVIC_SYSTICK_INT                                    ( 0x00000002UL )\r
74 #define portNVIC_SYSTICK_ENABLE                                 ( 0x00000001UL )\r
75 #define portNVIC_PENDSV_PRI                                             ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )\r
76 #define portNVIC_SYSTICK_PRI                                    ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )\r
77 #define portNVIC_SVC_PRI                                                ( ( ( uint32_t ) configMAX_SYSCALL_INTERRUPT_PRIORITY - 1UL ) << 24UL )\r
78 \r
79 /* Constants required to manipulate the VFP. */\r
80 #define portFPCCR                                                               ( ( volatile uint32_t * ) 0xe000ef34UL ) /* Floating point context control register. */\r
81 #define portASPEN_AND_LSPEN_BITS                                ( 0x3UL << 30UL )\r
82 \r
83 /* Constants required to set up the initial stack. */\r
84 #define portINITIAL_XPSR                                                ( 0x01000000UL )\r
85 #define portINITIAL_EXC_RETURN                                  ( 0xfffffffdUL )\r
86 #define portINITIAL_CONTROL_IF_UNPRIVILEGED             ( 0x03 )\r
87 #define portINITIAL_CONTROL_IF_PRIVILEGED               ( 0x02 )\r
88 \r
89 /* Constants required to check the validity of an interrupt priority. */\r
90 #define portFIRST_USER_INTERRUPT_NUMBER         ( 16 )\r
91 #define portNVIC_IP_REGISTERS_OFFSET_16         ( 0xE000E3F0 )\r
92 #define portAIRCR_REG                                           ( * ( ( volatile uint32_t * ) 0xE000ED0C ) )\r
93 #define portMAX_8_BIT_VALUE                                     ( ( uint8_t ) 0xff )\r
94 #define portTOP_BIT_OF_BYTE                                     ( ( uint8_t ) 0x80 )\r
95 #define portMAX_PRIGROUP_BITS                           ( ( uint8_t ) 7 )\r
96 #define portPRIORITY_GROUP_MASK                         ( 0x07UL << 8UL )\r
97 #define portPRIGROUP_SHIFT                                      ( 8UL )\r
98 \r
99 /* Offsets in the stack to the parameters when inside the SVC handler. */\r
100 #define portOFFSET_TO_PC                                                ( 6 )\r
101 \r
102 /* For strict compliance with the Cortex-M spec the task start address should\r
103 have bit-0 clear, as it is loaded into the PC on exit from an ISR. */\r
104 #define portSTART_ADDRESS_MASK                          ( ( StackType_t ) 0xfffffffeUL )\r
105 \r
106 /* Each task maintains its own interrupt status in the critical nesting\r
107 variable.  Note this is not saved as part of the task context as context\r
108 switches can only occur when uxCriticalNesting is zero. */\r
109 static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;\r
110 \r
111 /*\r
112  * Setup the timer to generate the tick interrupts.\r
113  */\r
114 static void prvSetupTimerInterrupt( void ) PRIVILEGED_FUNCTION;\r
115 \r
116 /*\r
117  * Configure a number of standard MPU regions that are used by all tasks.\r
118  */\r
119 static void prvSetupMPU( void ) PRIVILEGED_FUNCTION;\r
120 \r
121 /*\r
122  * Start first task is a separate function so it can be tested in isolation.\r
123  */\r
124 static void prvStartFirstTask( void ) PRIVILEGED_FUNCTION;\r
125 \r
126 /*\r
127  * Return the smallest MPU region size that a given number of bytes will fit\r
128  * into.  The region size is returned as the value that should be programmed\r
129  * into the region attribute register for that region.\r
130  */\r
131 static uint32_t prvGetMPURegionSizeSetting( uint32_t ulActualSizeInBytes ) PRIVILEGED_FUNCTION;\r
132 \r
133 /*\r
134  * Checks to see if being called from the context of an unprivileged task, and\r
135  * if so raises the privilege level and returns false - otherwise does nothing\r
136  * other than return true.\r
137  */\r
138 BaseType_t xPortRaisePrivilege( void );\r
139 \r
140 /*\r
141  * Standard FreeRTOS exception handlers.\r
142  */\r
143 void xPortPendSVHandler( void ) PRIVILEGED_FUNCTION;\r
144 void xPortSysTickHandler( void ) PRIVILEGED_FUNCTION;\r
145 void vPortSVCHandler( void ) PRIVILEGED_FUNCTION;\r
146 \r
147 /*\r
148  * Starts the scheduler by restoring the context of the first task to run.\r
149  */\r
150 static void prvRestoreContextOfFirstTask( void ) PRIVILEGED_FUNCTION;\r
151 \r
152 /*\r
153  * C portion of the SVC handler.  The SVC handler is split between an asm entry\r
154  * and a C wrapper for simplicity of coding and maintenance.\r
155  */\r
156 void prvSVCHandler( uint32_t *pulRegisters ) __attribute__((used)) PRIVILEGED_FUNCTION;\r
157 \r
158 /*\r
159  * Function to enable the VFP.\r
160  */\r
161 static void vPortEnableVFP( void );\r
162 \r
163 /*\r
164  * Utility function.\r
165  */\r
166 static uint32_t prvPortGetIPSR( void );\r
167 \r
168 /*\r
169  * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure\r
170  * FreeRTOS API functions are not called from interrupts that have been assigned\r
171  * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY.\r
172  */\r
173 #if ( configASSERT_DEFINED == 1 )\r
174          static uint8_t ucMaxSysCallPriority = 0;\r
175          static uint32_t ulMaxPRIGROUPValue = 0;\r
176          static const volatile uint8_t * const pcInterruptPriorityRegisters = ( const uint8_t * ) portNVIC_IP_REGISTERS_OFFSET_16;\r
177 #endif /* configASSERT_DEFINED */\r
178 \r
179 /*-----------------------------------------------------------*/\r
180 \r
181 /*\r
182  * See header file for description.\r
183  */\r
184 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged )\r
185 {\r
186         /* Simulate the stack frame as it would be created by a context switch\r
187         interrupt. */\r
188         pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */\r
189         *pxTopOfStack = portINITIAL_XPSR;       /* xPSR */\r
190         pxTopOfStack--;\r
191         *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;    /* PC */\r
192         pxTopOfStack--;\r
193         *pxTopOfStack = 0;      /* LR */\r
194         pxTopOfStack -= 5;      /* R12, R3, R2 and R1. */\r
195         *pxTopOfStack = ( StackType_t ) pvParameters;   /* R0 */\r
196 \r
197         /* A save method is being used that requires each task to maintain its\r
198         own exec return value. */\r
199         pxTopOfStack--;\r
200         *pxTopOfStack = portINITIAL_EXC_RETURN;\r
201 \r
202         pxTopOfStack -= 9;      /* R11, R10, R9, R8, R7, R6, R5 and R4. */\r
203 \r
204         if( xRunPrivileged == pdTRUE )\r
205         {\r
206                 *pxTopOfStack = portINITIAL_CONTROL_IF_PRIVILEGED;\r
207         }\r
208         else\r
209         {\r
210                 *pxTopOfStack = portINITIAL_CONTROL_IF_UNPRIVILEGED;\r
211         }\r
212 \r
213         return pxTopOfStack;\r
214 }\r
215 /*-----------------------------------------------------------*/\r
216 \r
217 void prvSVCHandler( uint32_t *pulParam )\r
218 {\r
219 uint8_t ucSVCNumber;\r
220 uint32_t ulReg;\r
221 \r
222         /* The stack contains: r0, r1, r2, r3, r12, r14, the return address and\r
223         xPSR.  The first argument (r0) is pulParam[ 0 ]. */\r
224         ucSVCNumber = ( ( uint8_t * ) pulParam[ portOFFSET_TO_PC ] )[ -2 ];\r
225         switch( ucSVCNumber )\r
226         {\r
227                 case portSVC_START_SCHEDULER    :       portNVIC_SYSPRI1_REG |= portNVIC_SVC_PRI;\r
228                                                                                         prvRestoreContextOfFirstTask();\r
229                                                                                         break;\r
230 \r
231                 case portSVC_YIELD                              :       portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;\r
232                                                                                         /* Barriers are normally not required\r
233                                                                                         but do ensure the code is completely\r
234                                                                                         within the specified behaviour for the\r
235                                                                                         architecture. */\r
236                                                                                         __asm volatile( "dsb" );\r
237                                                                                         __asm volatile( "isb" );\r
238 \r
239                                                                                         break;\r
240 \r
241                 case portSVC_RAISE_PRIVILEGE    :       __asm\r
242                                                                                         {\r
243                                                                                                 mrs ulReg, control      /* Obtain current control value. */\r
244                                                                                                 bic ulReg, #1           /* Set privilege bit. */\r
245                                                                                                 msr control, ulReg      /* Write back new control value. */\r
246                                                                                         }\r
247                                                                                         break;\r
248 \r
249                 default                                                 :       /* Unknown SVC call. */\r
250                                                                                         break;\r
251         }\r
252 }\r
253 /*-----------------------------------------------------------*/\r
254 \r
255 __asm void vPortSVCHandler( void )\r
256 {\r
257         extern prvSVCHandler\r
258 \r
259         PRESERVE8\r
260 \r
261         /* Assumes psp was in use. */\r
262         #ifndef USE_PROCESS_STACK       /* Code should not be required if a main() is using the process stack. */\r
263                 tst lr, #4\r
264                 ite eq\r
265                 mrseq r0, msp\r
266                 mrsne r0, psp\r
267         #else\r
268                 mrs r0, psp\r
269         #endif\r
270                 b prvSVCHandler\r
271 }\r
272 /*-----------------------------------------------------------*/\r
273 \r
274 __asm void prvRestoreContextOfFirstTask( void )\r
275 {\r
276         PRESERVE8\r
277 \r
278         ldr r0, =0xE000ED08                             /* Use the NVIC offset register to locate the stack. */\r
279         ldr r0, [r0]\r
280         ldr r0, [r0]\r
281         msr msp, r0                                             /* Set the msp back to the start of the stack. */\r
282         ldr     r3, =pxCurrentTCB                       /* Restore the context. */\r
283         ldr r1, [r3]\r
284         ldr r0, [r1]                                    /* The first item in the TCB is the task top of stack. */\r
285         add r1, r1, #4                                  /* Move onto the second item in the TCB... */\r
286         ldr r2, =0xe000ed9c                             /* Region Base Address register. */\r
287         ldmia r1!, {r4-r11}                             /* Read 4 sets of MPU registers. */\r
288         stmia r2!, {r4-r11}                             /* Write 4 sets of MPU registers. */\r
289         ldmia r0!, {r3-r11, r14}        /* Pop the registers that are not automatically saved on exception entry. */\r
290         msr control, r3\r
291         msr psp, r0                                             /* Restore the task stack pointer. */\r
292         mov r0, #0\r
293         msr     basepri, r0\r
294         bx r14\r
295         nop\r
296 }\r
297 /*-----------------------------------------------------------*/\r
298 \r
299 /*\r
300  * See header file for description.\r
301  */\r
302 BaseType_t xPortStartScheduler( void )\r
303 {\r
304         /* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0.  See\r
305         http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */\r
306         configASSERT( ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) );\r
307 \r
308         #if( configASSERT_DEFINED == 1 )\r
309         {\r
310                 volatile uint32_t ulOriginalPriority;\r
311                 volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER );\r
312                 volatile uint8_t ucMaxPriorityValue;\r
313 \r
314                 /* Determine the maximum priority from which ISR safe FreeRTOS API\r
315                 functions can be called.  ISR safe functions are those that end in\r
316                 "FromISR".  FreeRTOS maintains separate thread and ISR API functions to\r
317                 ensure interrupt entry is as fast and simple as possible.\r
318 \r
319                 Save the interrupt priority value that is about to be clobbered. */\r
320                 ulOriginalPriority = *pucFirstUserPriorityRegister;\r
321 \r
322                 /* Determine the number of priority bits available.  First write to all\r
323                 possible bits. */\r
324                 *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;\r
325 \r
326                 /* Read the value back to see how many bits stuck. */\r
327                 ucMaxPriorityValue = *pucFirstUserPriorityRegister;\r
328 \r
329                 /* Use the same mask on the maximum system call priority. */\r
330                 ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;\r
331 \r
332                 /* Calculate the maximum acceptable priority group value for the number\r
333                 of bits read back. */\r
334                 ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;\r
335                 while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )\r
336                 {\r
337                         ulMaxPRIGROUPValue--;\r
338                         ucMaxPriorityValue <<= ( uint8_t ) 0x01;\r
339                 }\r
340 \r
341                 #ifdef __NVIC_PRIO_BITS\r
342                 {\r
343                         /* Check the CMSIS configuration that defines the number of\r
344                         priority bits matches the number of priority bits actually queried\r
345                         from the hardware. */\r
346                         configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS );\r
347                 }\r
348                 #endif\r
349 \r
350                 #ifdef configPRIO_BITS\r
351                 {\r
352                         /* Check the FreeRTOS configuration that defines the number of\r
353                         priority bits matches the number of priority bits actually queried\r
354                         from the hardware. */\r
355                         configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS );\r
356                 }\r
357                 #endif\r
358 \r
359                 /* Shift the priority group value back to its position within the AIRCR\r
360                 register. */\r
361                 ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;\r
362                 ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;\r
363 \r
364                 /* Restore the clobbered interrupt priority register to its original\r
365                 value. */\r
366                 *pucFirstUserPriorityRegister = ulOriginalPriority;\r
367         }\r
368         #endif /* conifgASSERT_DEFINED */\r
369 \r
370         /* Make PendSV and SysTick the same priority as the kernel, and the SVC\r
371         handler higher priority so it can be used to exit a critical section (where\r
372         lower priorities are masked). */\r
373         portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;\r
374         portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;\r
375 \r
376         /* Configure the regions in the MPU that are common to all tasks. */\r
377         prvSetupMPU();\r
378 \r
379         /* Start the timer that generates the tick ISR.  Interrupts are disabled\r
380         here already. */\r
381         prvSetupTimerInterrupt();\r
382 \r
383         /* Initialise the critical nesting count ready for the first task. */\r
384         uxCriticalNesting = 0;\r
385 \r
386         /* Ensure the VFP is enabled - it should be anyway. */\r
387         vPortEnableVFP();\r
388 \r
389         /* Lazy save always. */\r
390         *( portFPCCR ) |= portASPEN_AND_LSPEN_BITS;\r
391 \r
392         /* Start the first task. */\r
393         prvStartFirstTask();\r
394 \r
395         /* Should not get here! */\r
396         return 0;\r
397 }\r
398 /*-----------------------------------------------------------*/\r
399 \r
400 __asm void prvStartFirstTask( void )\r
401 {\r
402         PRESERVE8\r
403 \r
404         /* Use the NVIC offset register to locate the stack. */\r
405         ldr r0, =0xE000ED08\r
406         ldr r0, [r0]\r
407         ldr r0, [r0]\r
408         /* Set the msp back to the start of the stack. */\r
409         msr msp, r0\r
410         /* Clear the bit that indicates the FPU is in use in case the FPU was used\r
411         before the scheduler was started - which would otherwise result in the\r
412         unnecessary leaving of space in the SVC stack for lazy saving of FPU\r
413         registers. */\r
414         mov r0, #0\r
415         msr control, r0\r
416         /* Globally enable interrupts. */\r
417         cpsie i\r
418         cpsie f\r
419         dsb\r
420         isb\r
421         svc portSVC_START_SCHEDULER     /* System call to start first task. */\r
422         nop\r
423         nop\r
424 }\r
425 \r
426 void vPortEndScheduler( void )\r
427 {\r
428         /* Not implemented in ports where there is nothing to return to.\r
429         Artificially force an assert. */\r
430         configASSERT( uxCriticalNesting == 1000UL );\r
431 }\r
432 /*-----------------------------------------------------------*/\r
433 \r
434 void vPortEnterCritical( void )\r
435 {\r
436 BaseType_t xRunningPrivileged = xPortRaisePrivilege();\r
437 \r
438         portDISABLE_INTERRUPTS();\r
439         uxCriticalNesting++;\r
440 \r
441         vPortResetPrivilege( xRunningPrivileged );\r
442 }\r
443 /*-----------------------------------------------------------*/\r
444 \r
445 void vPortExitCritical( void )\r
446 {\r
447 BaseType_t xRunningPrivileged = xPortRaisePrivilege();\r
448 \r
449         configASSERT( uxCriticalNesting );\r
450         uxCriticalNesting--;\r
451         if( uxCriticalNesting == 0 )\r
452         {\r
453                 portENABLE_INTERRUPTS();\r
454         }\r
455         vPortResetPrivilege( xRunningPrivileged );\r
456 }\r
457 /*-----------------------------------------------------------*/\r
458 \r
459 __asm void xPortPendSVHandler( void )\r
460 {\r
461         extern uxCriticalNesting;\r
462         extern pxCurrentTCB;\r
463         extern vTaskSwitchContext;\r
464 \r
465         PRESERVE8\r
466 \r
467         mrs r0, psp\r
468 \r
469         ldr r3, =pxCurrentTCB                   /* Get the location of the current TCB. */\r
470         ldr r2, [r3]\r
471 \r
472         tst r14, #0x10                                  /* Is the task using the FPU context?  If so, push high vfp registers. */\r
473         it eq\r
474         vstmdbeq r0!, {s16-s31}\r
475 \r
476         mrs r1, control\r
477         stmdb r0!, {r1, r4-r11, r14}    /* Save the remaining registers. */\r
478         str r0, [r2]                                    /* Save the new top of stack into the first member of the TCB. */\r
479 \r
480         stmdb sp!, {r0, r3}\r
481         mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY\r
482         msr basepri, r0\r
483         dsb\r
484         isb\r
485         bl vTaskSwitchContext\r
486         mov r0, #0\r
487         msr basepri, r0\r
488         ldmia sp!, {r0, r3}\r
489                                                                         /* Restore the context. */\r
490         ldr r1, [r3]\r
491         ldr r0, [r1]                                    /* The first item in the TCB is the task top of stack. */\r
492         add r1, r1, #4                                  /* Move onto the second item in the TCB... */\r
493         ldr r2, =0xe000ed9c                             /* Region Base Address register. */\r
494         ldmia r1!, {r4-r11}                             /* Read 4 sets of MPU registers. */\r
495         stmia r2!, {r4-r11}                             /* Write 4 sets of MPU registers. */\r
496         ldmia r0!, {r3-r11, r14}                /* Pop the registers that are not automatically saved on exception entry. */\r
497         msr control, r3\r
498 \r
499         tst r14, #0x10                                  /* Is the task using the FPU context?  If so, pop the high vfp registers too. */\r
500         it eq\r
501         vldmiaeq r0!, {s16-s31}\r
502 \r
503         msr psp, r0\r
504         bx r14\r
505         nop\r
506 }\r
507 /*-----------------------------------------------------------*/\r
508 \r
509 void xPortSysTickHandler( void )\r
510 {\r
511 uint32_t ulDummy;\r
512 \r
513         ulDummy = portSET_INTERRUPT_MASK_FROM_ISR();\r
514         {\r
515                 /* Increment the RTOS tick. */\r
516                 if( xTaskIncrementTick() != pdFALSE )\r
517                 {\r
518                         /* Pend a context switch. */\r
519                         portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;\r
520                 }\r
521         }\r
522         portCLEAR_INTERRUPT_MASK_FROM_ISR( ulDummy );\r
523 }\r
524 /*-----------------------------------------------------------*/\r
525 \r
526 /*\r
527  * Setup the systick timer to generate the tick interrupts at the required\r
528  * frequency.\r
529  */\r
530 static void prvSetupTimerInterrupt( void )\r
531 {\r
532         /* Reset the SysTick. */\r
533         portNVIC_SYSTICK_CTRL_REG = 0UL;\r
534         portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;\r
535 \r
536         /* Configure SysTick to interrupt at the requested rate. */\r
537         portNVIC_SYSTICK_LOAD_REG = ( configCPU_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;\r
538         portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE;\r
539 }\r
540 /*-----------------------------------------------------------*/\r
541 \r
542 __asm void vPortSwitchToUserMode( void )\r
543 {\r
544         PRESERVE8\r
545 \r
546         mrs r0, control\r
547         orr r0, #1\r
548         msr control, r0\r
549         bx r14\r
550 }\r
551 /*-----------------------------------------------------------*/\r
552 \r
553 __asm void vPortEnableVFP( void )\r
554 {\r
555         PRESERVE8\r
556 \r
557         ldr.w r0, =0xE000ED88           /* The FPU enable bits are in the CPACR. */\r
558         ldr r1, [r0]\r
559 \r
560         orr r1, r1, #( 0xf << 20 )      /* Enable CP10 and CP11 coprocessors, then save back. */\r
561         str r1, [r0]\r
562         bx r14\r
563         nop\r
564         nop\r
565 }\r
566 /*-----------------------------------------------------------*/\r
567 \r
568 static void prvSetupMPU( void )\r
569 {\r
570 extern uint32_t __privileged_functions_end__;\r
571 extern uint32_t __FLASH_segment_start__;\r
572 extern uint32_t __FLASH_segment_end__;\r
573 extern uint32_t __privileged_data_start__;\r
574 extern uint32_t __privileged_data_end__;\r
575 \r
576         /* Check the expected MPU is present. */\r
577         if( portMPU_TYPE_REG == portEXPECTED_MPU_TYPE_VALUE )\r
578         {\r
579                 /* First setup the entire flash for unprivileged read only access. */\r
580                 portMPU_REGION_BASE_ADDRESS_REG =       ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */\r
581                                                                                         ( portMPU_REGION_VALID ) |\r
582                                                                                         ( portUNPRIVILEGED_FLASH_REGION );\r
583 \r
584                 portMPU_REGION_ATTRIBUTE_REG =  ( portMPU_REGION_READ_ONLY ) |\r
585                                                                                 ( portMPU_REGION_CACHEABLE_BUFFERABLE ) |\r
586                                                                                 ( prvGetMPURegionSizeSetting( ( uint32_t ) __FLASH_segment_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) |\r
587                                                                                 ( portMPU_REGION_ENABLE );\r
588 \r
589                 /* Setup the first 16K for privileged only access (even though less\r
590                 than 10K is actually being used).  This is where the kernel code is\r
591                 placed. */\r
592                 portMPU_REGION_BASE_ADDRESS_REG =       ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */\r
593                                                                                         ( portMPU_REGION_VALID ) |\r
594                                                                                         ( portPRIVILEGED_FLASH_REGION );\r
595 \r
596                 portMPU_REGION_ATTRIBUTE_REG =  ( portMPU_REGION_PRIVILEGED_READ_ONLY ) |\r
597                                                                                 ( portMPU_REGION_CACHEABLE_BUFFERABLE ) |\r
598                                                                                 ( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) |\r
599                                                                                 ( portMPU_REGION_ENABLE );\r
600 \r
601                 /* Setup the privileged data RAM region.  This is where the kernel data\r
602                 is placed. */\r
603                 portMPU_REGION_BASE_ADDRESS_REG =       ( ( uint32_t ) __privileged_data_start__ ) | /* Base address. */\r
604                                                                                         ( portMPU_REGION_VALID ) |\r
605                                                                                         ( portPRIVILEGED_RAM_REGION );\r
606 \r
607                 portMPU_REGION_ATTRIBUTE_REG =  ( portMPU_REGION_PRIVILEGED_READ_WRITE ) |\r
608                                                                                 ( portMPU_REGION_CACHEABLE_BUFFERABLE ) |\r
609                                                                                 prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_data_end__ - ( uint32_t ) __privileged_data_start__ ) |\r
610                                                                                 ( portMPU_REGION_ENABLE );\r
611 \r
612                 /* By default allow everything to access the general peripherals.  The\r
613                 system peripherals and registers are protected. */\r
614                 portMPU_REGION_BASE_ADDRESS_REG =       ( portPERIPHERALS_START_ADDRESS ) |\r
615                                                                                         ( portMPU_REGION_VALID ) |\r
616                                                                                         ( portGENERAL_PERIPHERALS_REGION );\r
617 \r
618                 portMPU_REGION_ATTRIBUTE_REG =  ( portMPU_REGION_READ_WRITE | portMPU_REGION_EXECUTE_NEVER ) |\r
619                                                                                 ( prvGetMPURegionSizeSetting( portPERIPHERALS_END_ADDRESS - portPERIPHERALS_START_ADDRESS ) ) |\r
620                                                                                 ( portMPU_REGION_ENABLE );\r
621 \r
622                 /* Enable the memory fault exception. */\r
623                 portNVIC_SYS_CTRL_STATE_REG |= portNVIC_MEM_FAULT_ENABLE;\r
624 \r
625                 /* Enable the MPU with the background region configured. */\r
626                 portMPU_CTRL_REG |= ( portMPU_ENABLE | portMPU_BACKGROUND_ENABLE );\r
627         }\r
628 }\r
629 /*-----------------------------------------------------------*/\r
630 \r
631 static uint32_t prvGetMPURegionSizeSetting( uint32_t ulActualSizeInBytes )\r
632 {\r
633 uint32_t ulRegionSize, ulReturnValue = 4;\r
634 \r
635         /* 32 is the smallest region size, 31 is the largest valid value for\r
636         ulReturnValue. */\r
637         for( ulRegionSize = 32UL; ulReturnValue < 31UL; ( ulRegionSize <<= 1UL ) )\r
638         {\r
639                 if( ulActualSizeInBytes <= ulRegionSize )\r
640                 {\r
641                         break;\r
642                 }\r
643                 else\r
644                 {\r
645                         ulReturnValue++;\r
646                 }\r
647         }\r
648 \r
649         /* Shift the code by one before returning so it can be written directly\r
650         into the the correct bit position of the attribute register. */\r
651         return ( ulReturnValue << 1UL );\r
652 }\r
653 /*-----------------------------------------------------------*/\r
654 \r
655 __asm BaseType_t xPortRaisePrivilege( void )\r
656 {\r
657         mrs r0, control\r
658         tst r0, #1                                              /* Is the task running privileged? */\r
659         itte ne\r
660         movne r0, #0                                    /* CONTROL[0]!=0, return false. */\r
661         svcne portSVC_RAISE_PRIVILEGE   /* Switch to privileged. */\r
662         moveq r0, #1                                    /* CONTROL[0]==0, return true. */\r
663         bx lr\r
664 }\r
665 /*-----------------------------------------------------------*/\r
666 \r
667 void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, StackType_t *pxBottomOfStack, uint32_t ulStackDepth )\r
668 {\r
669 extern uint32_t __SRAM_segment_start__;\r
670 extern uint32_t __SRAM_segment_end__;\r
671 extern uint32_t __privileged_data_start__;\r
672 extern uint32_t __privileged_data_end__;\r
673 \r
674 \r
675 int32_t lIndex;\r
676 uint32_t ul;\r
677 \r
678         if( xRegions == NULL )\r
679         {\r
680                 /* No MPU regions are specified so allow access to all RAM. */\r
681                 xMPUSettings->xRegion[ 0 ].ulRegionBaseAddress =\r
682                                 ( ( uint32_t ) __SRAM_segment_start__ ) | /* Base address. */\r
683                                 ( portMPU_REGION_VALID ) |\r
684                                 ( portSTACK_REGION );\r
685 \r
686                 xMPUSettings->xRegion[ 0 ].ulRegionAttribute =\r
687                                 ( portMPU_REGION_READ_WRITE ) |\r
688                                 ( portMPU_REGION_CACHEABLE_BUFFERABLE ) |\r
689                                 ( prvGetMPURegionSizeSetting( ( uint32_t ) __SRAM_segment_end__ - ( uint32_t ) __SRAM_segment_start__ ) ) |\r
690                                 ( portMPU_REGION_ENABLE );\r
691 \r
692                 /* Re-instate the privileged only RAM region as xRegion[ 0 ] will have\r
693                 just removed the privileged only parameters. */\r
694                 xMPUSettings->xRegion[ 1 ].ulRegionBaseAddress =\r
695                                 ( ( uint32_t ) __privileged_data_start__ ) | /* Base address. */\r
696                                 ( portMPU_REGION_VALID ) |\r
697                                 ( portSTACK_REGION + 1 );\r
698 \r
699                 xMPUSettings->xRegion[ 1 ].ulRegionAttribute =\r
700                                 ( portMPU_REGION_PRIVILEGED_READ_WRITE ) |\r
701                                 ( portMPU_REGION_CACHEABLE_BUFFERABLE ) |\r
702                                 prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_data_end__ - ( uint32_t ) __privileged_data_start__ ) |\r
703                                 ( portMPU_REGION_ENABLE );\r
704 \r
705                 /* Invalidate all other regions. */\r
706                 for( ul = 2; ul <= portNUM_CONFIGURABLE_REGIONS; ul++ )\r
707                 {\r
708                         xMPUSettings->xRegion[ ul ].ulRegionBaseAddress = ( portSTACK_REGION + ul ) | portMPU_REGION_VALID;\r
709                         xMPUSettings->xRegion[ ul ].ulRegionAttribute = 0UL;\r
710                 }\r
711         }\r
712         else\r
713         {\r
714                 /* This function is called automatically when the task is created - in\r
715                 which case the stack region parameters will be valid.  At all other\r
716                 times the stack parameters will not be valid and it is assumed that the\r
717                 stack region has already been configured. */\r
718                 if( ulStackDepth > 0 )\r
719                 {\r
720                         /* Define the region that allows access to the stack. */\r
721                         xMPUSettings->xRegion[ 0 ].ulRegionBaseAddress =\r
722                                         ( ( uint32_t ) pxBottomOfStack ) |\r
723                                         ( portMPU_REGION_VALID ) |\r
724                                         ( portSTACK_REGION ); /* Region number. */\r
725 \r
726                         xMPUSettings->xRegion[ 0 ].ulRegionAttribute =\r
727                                         ( portMPU_REGION_READ_WRITE ) | /* Read and write. */\r
728                                         ( prvGetMPURegionSizeSetting( ulStackDepth * ( uint32_t ) sizeof( StackType_t ) ) ) |\r
729                                         ( portMPU_REGION_CACHEABLE_BUFFERABLE ) |\r
730                                         ( portMPU_REGION_ENABLE );\r
731                 }\r
732 \r
733                 lIndex = 0;\r
734 \r
735                 for( ul = 1; ul <= portNUM_CONFIGURABLE_REGIONS; ul++ )\r
736                 {\r
737                         if( ( xRegions[ lIndex ] ).ulLengthInBytes > 0UL )\r
738                         {\r
739                                 /* Translate the generic region definition contained in\r
740                                 xRegions into the CM3 specific MPU settings that are then\r
741                                 stored in xMPUSettings. */\r
742                                 xMPUSettings->xRegion[ ul ].ulRegionBaseAddress =\r
743                                                 ( ( uint32_t ) xRegions[ lIndex ].pvBaseAddress ) |\r
744                                                 ( portMPU_REGION_VALID ) |\r
745                                                 ( portSTACK_REGION + ul ); /* Region number. */\r
746 \r
747                                 xMPUSettings->xRegion[ ul ].ulRegionAttribute =\r
748                                                 ( prvGetMPURegionSizeSetting( xRegions[ lIndex ].ulLengthInBytes ) ) |\r
749                                                 ( xRegions[ lIndex ].ulParameters ) |\r
750                                                 ( portMPU_REGION_ENABLE );\r
751                         }\r
752                         else\r
753                         {\r
754                                 /* Invalidate the region. */\r
755                                 xMPUSettings->xRegion[ ul ].ulRegionBaseAddress = ( portSTACK_REGION + ul ) | portMPU_REGION_VALID;\r
756                                 xMPUSettings->xRegion[ ul ].ulRegionAttribute = 0UL;\r
757                         }\r
758 \r
759                         lIndex++;\r
760                 }\r
761         }\r
762 }\r
763 /*-----------------------------------------------------------*/\r
764 \r
765 __asm uint32_t prvPortGetIPSR( void )\r
766 {\r
767         PRESERVE8\r
768 \r
769         mrs r0, ipsr\r
770         bx r14\r
771 }\r
772 /*-----------------------------------------------------------*/\r
773 \r
774 #if( configASSERT_DEFINED == 1 )\r
775 \r
776         void vPortValidateInterruptPriority( void )\r
777         {\r
778         uint32_t ulCurrentInterrupt;\r
779         uint8_t ucCurrentPriority;\r
780 \r
781                 /* Obtain the number of the currently executing interrupt. */\r
782                 ulCurrentInterrupt = prvPortGetIPSR();\r
783 \r
784                 /* Is the interrupt number a user defined interrupt? */\r
785                 if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER )\r
786                 {\r
787                         /* Look up the interrupt's priority. */\r
788                         ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ];\r
789 \r
790                         /* The following assertion will fail if a service routine (ISR) for\r
791                         an interrupt that has been assigned a priority above\r
792                         configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API\r
793                         function.  ISR safe FreeRTOS API functions must *only* be called\r
794                         from interrupts that have been assigned a priority at or below\r
795                         configMAX_SYSCALL_INTERRUPT_PRIORITY.\r
796 \r
797                         Numerically low interrupt priority numbers represent logically high\r
798                         interrupt priorities, therefore the priority of the interrupt must\r
799                         be set to a value equal to or numerically *higher* than\r
800                         configMAX_SYSCALL_INTERRUPT_PRIORITY.\r
801 \r
802                         Interrupts that use the FreeRTOS API must not be left at their\r
803                         default priority of     zero as that is the highest possible priority,\r
804                         which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY,\r
805                         and     therefore also guaranteed to be invalid.\r
806 \r
807                         FreeRTOS maintains separate thread and ISR API functions to ensure\r
808                         interrupt entry is as fast and simple as possible.\r
809 \r
810                         The following links provide detailed information:\r
811                         http://www.freertos.org/RTOS-Cortex-M3-M4.html\r
812                         http://www.freertos.org/FAQHelp.html */\r
813                         configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );\r
814                 }\r
815 \r
816                 /* Priority grouping:  The interrupt controller (NVIC) allows the bits\r
817                 that define each interrupt's priority to be split between bits that\r
818                 define the interrupt's pre-emption priority bits and bits that define\r
819                 the interrupt's sub-priority.  For simplicity all bits must be defined\r
820                 to be pre-emption priority bits.  The following assertion will fail if\r
821                 this is not the case (if some bits represent a sub-priority).\r
822 \r
823                 If the application only uses CMSIS libraries for interrupt\r
824                 configuration then the correct setting can be achieved on all Cortex-M\r
825                 devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the\r
826                 scheduler.  Note however that some vendor specific peripheral libraries\r
827                 assume a non-zero priority group setting, in which cases using a value\r
828                 of zero will result in unpredicable behaviour. */\r
829                 configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue );\r
830         }\r
831 \r
832 #endif /* configASSERT_DEFINED */\r
833 \r
834 \r