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