]> git.sur5r.net Git - freertos/blob - FreeRTOS/Source/portable/GCC/ARM_CM3_MPU/port.c
Updates to CM3_MPU GCC port
[freertos] / FreeRTOS / Source / portable / GCC / ARM_CM3_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 #undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE\r
42 \r
43 #ifndef configSYSTICK_CLOCK_HZ\r
44         #define configSYSTICK_CLOCK_HZ configCPU_CLOCK_HZ\r
45         /* Ensure the SysTick is clocked at the same frequency as the core. */\r
46         #define portNVIC_SYSTICK_CLK    ( 1UL << 2UL )\r
47 #else\r
48         /* The way the SysTick is clocked is not modified in case it is not the same\r
49          * as the core. */\r
50         #define portNVIC_SYSTICK_CLK    ( 0 )\r
51 #endif\r
52 \r
53 /* Constants required to access and manipulate the NVIC. */\r
54 #define portNVIC_SYSTICK_CTRL_REG                               ( * ( ( volatile uint32_t * ) 0xe000e010 ) )\r
55 #define portNVIC_SYSTICK_LOAD_REG                               ( * ( ( volatile uint32_t * ) 0xe000e014 ) )\r
56 #define portNVIC_SYSTICK_CURRENT_VALUE_REG              ( * ( ( volatile uint32_t * ) 0xe000e018 ) )\r
57 #define portNVIC_SYSPRI2_REG                                    ( *     ( ( volatile uint32_t * ) 0xe000ed20 ) )\r
58 #define portNVIC_SYSPRI1_REG                                    ( * ( ( volatile uint32_t * ) 0xe000ed1c ) )\r
59 #define portNVIC_SYS_CTRL_STATE_REG                             ( * ( ( volatile uint32_t * ) 0xe000ed24 ) )\r
60 #define portNVIC_MEM_FAULT_ENABLE                               ( 1UL << 16UL )\r
61 \r
62 /* Constants required to access and manipulate the MPU. */\r
63 #define portMPU_TYPE_REG                                                ( * ( ( volatile uint32_t * ) 0xe000ed90 ) )\r
64 #define portMPU_REGION_BASE_ADDRESS_REG                 ( * ( ( volatile uint32_t * ) 0xe000ed9C ) )\r
65 #define portMPU_REGION_ATTRIBUTE_REG                    ( * ( ( volatile uint32_t * ) 0xe000edA0 ) )\r
66 #define portMPU_CTRL_REG                                                ( * ( ( volatile uint32_t * ) 0xe000ed94 ) )\r
67 #define portEXPECTED_MPU_TYPE_VALUE                             ( 8UL << 8UL ) /* 8 regions, unified. */\r
68 #define portMPU_ENABLE                                                  ( 0x01UL )\r
69 #define portMPU_BACKGROUND_ENABLE                               ( 1UL << 2UL )\r
70 #define portPRIVILEGED_EXECUTION_START_ADDRESS  ( 0UL )\r
71 #define portMPU_REGION_VALID                                    ( 0x10UL )\r
72 #define portMPU_REGION_ENABLE                                   ( 0x01UL )\r
73 #define portPERIPHERALS_START_ADDRESS                   0x40000000UL\r
74 #define portPERIPHERALS_END_ADDRESS                             0x5FFFFFFFUL\r
75 \r
76 /* Constants required to access and manipulate the SysTick. */\r
77 #define portNVIC_SYSTICK_INT                                    ( 0x00000002UL )\r
78 #define portNVIC_SYSTICK_ENABLE                                 ( 0x00000001UL )\r
79 #define portNVIC_PENDSV_PRI                                             ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )\r
80 #define portNVIC_SYSTICK_PRI                                    ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )\r
81 #define portNVIC_SVC_PRI                                                ( ( ( uint32_t ) configMAX_SYSCALL_INTERRUPT_PRIORITY - 1UL ) << 24UL )\r
82 \r
83 /* Constants required to set up the initial stack. */\r
84 #define portINITIAL_XPSR                                                ( 0x01000000 )\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 \r
106 /*\r
107  * Configure a number of standard MPU regions that are used by all tasks.\r
108  */\r
109 static void prvSetupMPU( void ) PRIVILEGED_FUNCTION;\r
110 \r
111 /*\r
112  * Return the smallest MPU region size that a given number of bytes will fit\r
113  * into.  The region size is returned as the value that should be programmed\r
114  * into the region attribute register for that region.\r
115  */\r
116 static uint32_t prvGetMPURegionSizeSetting( uint32_t ulActualSizeInBytes ) PRIVILEGED_FUNCTION;\r
117 \r
118 /*\r
119  * Setup the timer to generate the tick interrupts.  The implementation in this\r
120  * file is weak to allow application writers to change the timer used to\r
121  * generate the tick interrupt.\r
122  */\r
123 void vPortSetupTimerInterrupt( void );\r
124 \r
125 /*\r
126  * Standard FreeRTOS exception handlers.\r
127  */\r
128 void xPortPendSVHandler( void ) __attribute__ (( naked )) PRIVILEGED_FUNCTION;\r
129 void xPortSysTickHandler( void )  __attribute__ ((optimize("3"))) PRIVILEGED_FUNCTION;\r
130 void vPortSVCHandler( void ) __attribute__ (( naked )) PRIVILEGED_FUNCTION;\r
131 \r
132 /*\r
133  * Starts the scheduler by restoring the context of the first task to run.\r
134  */\r
135 static void prvRestoreContextOfFirstTask( void ) __attribute__(( naked )) PRIVILEGED_FUNCTION;\r
136 \r
137 /*\r
138  * C portion of the SVC handler.  The SVC handler is split between an asm entry\r
139  * and a C wrapper for simplicity of coding and maintenance.\r
140  */\r
141 static void prvSVCHandler( uint32_t *pulRegisters ) __attribute__(( noinline )) PRIVILEGED_FUNCTION;\r
142 \r
143 /**\r
144  * @brief Checks whether or not the processor is privileged.\r
145  *\r
146  * @return 1 if the processor is already privileged, 0 otherwise.\r
147  */\r
148 BaseType_t xIsPrivileged( void ) __attribute__ (( naked ));\r
149 \r
150 /**\r
151  * @brief Lowers the privilege level by setting the bit 0 of the CONTROL\r
152  * register.\r
153  *\r
154  * Bit 0 of the CONTROL register defines the privilege level of Thread Mode.\r
155  *  Bit[0] = 0 --> The processor is running privileged\r
156  *  Bit[0] = 1 --> The processor is running unprivileged.\r
157  */\r
158 void vResetPrivilege( void ) __attribute__ (( naked ));\r
159 \r
160 /**\r
161  * @brief Calls the port specific code to raise the privilege.\r
162  *\r
163  * @return pdFALSE if privilege was raised, pdTRUE otherwise.\r
164  */\r
165 extern BaseType_t xPortRaisePrivilege( void );\r
166 \r
167 /**\r
168  * @brief If xRunningPrivileged is not pdTRUE, calls the port specific\r
169  * code to reset the privilege, otherwise does nothing.\r
170  */\r
171 extern void vPortResetPrivilege( BaseType_t xRunningPrivileged );\r
172 /*-----------------------------------------------------------*/\r
173 \r
174 /* Each task maintains its own interrupt status in the critical nesting\r
175  * variable.  Note this is not saved as part of the task context as context\r
176  * switches can only occur when uxCriticalNesting is zero. */\r
177 static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;\r
178 \r
179 /*\r
180  * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure\r
181  * FreeRTOS API functions are not called from interrupts that have been assigned\r
182  * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY.\r
183  */\r
184 #if ( configASSERT_DEFINED == 1 )\r
185          static uint8_t ucMaxSysCallPriority = 0;\r
186          static uint32_t ulMaxPRIGROUPValue = 0;\r
187          static const volatile uint8_t * const pcInterruptPriorityRegisters = ( const volatile uint8_t * const ) portNVIC_IP_REGISTERS_OFFSET_16;\r
188 #endif /* configASSERT_DEFINED */\r
189 /*-----------------------------------------------------------*/\r
190 \r
191 /*\r
192  * See header file for description.\r
193  */\r
194 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged )\r
195 {\r
196         /* Simulate the stack frame as it would be created by a context switch\r
197          * interrupt. */\r
198         pxTopOfStack--; /* Offset added to account for the way the MCU uses the stack on entry/exit of interrupts. */\r
199         *pxTopOfStack = portINITIAL_XPSR;       /* xPSR */\r
200         pxTopOfStack--;\r
201         *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK;    /* PC */\r
202         pxTopOfStack--;\r
203         *pxTopOfStack = 0;      /* LR */\r
204         pxTopOfStack -= 5;      /* R12, R3, R2 and R1. */\r
205         *pxTopOfStack = ( StackType_t ) pvParameters;   /* R0 */\r
206         pxTopOfStack -= 9;      /* R11, R10, R9, R8, R7, R6, R5 and R4. */\r
207 \r
208         if( xRunPrivileged == pdTRUE )\r
209         {\r
210                 *pxTopOfStack = portINITIAL_CONTROL_IF_PRIVILEGED;\r
211         }\r
212         else\r
213         {\r
214                 *pxTopOfStack = portINITIAL_CONTROL_IF_UNPRIVILEGED;\r
215         }\r
216 \r
217         return pxTopOfStack;\r
218 }\r
219 /*-----------------------------------------------------------*/\r
220 \r
221 void vPortSVCHandler( void )\r
222 {\r
223         /* Assumes psp was in use. */\r
224         __asm volatile\r
225         (\r
226                 #ifndef USE_PROCESS_STACK       /* Code should not be required if a main() is using the process stack. */\r
227                         "       tst lr, #4                                              \n"\r
228                         "       ite eq                                                  \n"\r
229                         "       mrseq r0, msp                                   \n"\r
230                         "       mrsne r0, psp                                   \n"\r
231                 #else\r
232                         "       mrs r0, psp                                             \n"\r
233                 #endif\r
234                         "       b %0                                                    \n"\r
235                         ::"i"(prvSVCHandler):"r0", "memory"\r
236         );\r
237 }\r
238 /*-----------------------------------------------------------*/\r
239 \r
240 static void prvSVCHandler(      uint32_t *pulParam )\r
241 {\r
242 uint8_t ucSVCNumber;\r
243 uint32_t ulPC;\r
244 #if( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 )\r
245         #if defined( __ARMCC_VERSION )\r
246                 /* Declaration when these variable are defined in code instead of being\r
247                  * exported from linker scripts. */\r
248                 extern uint32_t * __syscalls_flash_start__;\r
249                 extern uint32_t * __syscalls_flash_end__;\r
250         #else\r
251                 /* Declaration when these variable are exported from linker scripts. */\r
252                 extern uint32_t __syscalls_flash_start__[];\r
253                 extern uint32_t __syscalls_flash_end__[];\r
254         #endif /* #if defined( __ARMCC_VERSION ) */\r
255 #endif /* #if( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 ) */\r
256 \r
257         /* The stack contains: r0, r1, r2, r3, r12, LR, PC and xPSR.  The first\r
258          * argument (r0) is pulParam[ 0 ]. */\r
259         ulPC = pulParam[ portOFFSET_TO_PC ];\r
260         ucSVCNumber = ( ( uint8_t * ) ulPC )[ -2 ];\r
261 \r
262         switch( ucSVCNumber )\r
263         {\r
264                 case portSVC_START_SCHEDULER    :       portNVIC_SYSPRI1_REG |= portNVIC_SVC_PRI;\r
265                                                                                         prvRestoreContextOfFirstTask();\r
266                                                                                         break;\r
267 \r
268                 case portSVC_YIELD                              :       portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;\r
269                                                                                         /* Barriers are normally not required\r
270                                                                                          * but do ensure the code is completely\r
271                                                                                          * within the specified behaviour for the\r
272                                                                                          * architecture. */\r
273                                                                                         __asm volatile( "dsb" ::: "memory" );\r
274                                                                                         __asm volatile( "isb" );\r
275 \r
276                                                                                         break;\r
277 \r
278 \r
279         #if( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 )\r
280                 case portSVC_RAISE_PRIVILEGE    :       /* Only raise the privilege, if the\r
281                                                                                          * svc was raised from any of the\r
282                                                                                          * system calls. */\r
283                                                                                         if( ulPC >= ( uint32_t ) __syscalls_flash_start__ &&\r
284                                                                                                 ulPC <= ( uint32_t ) __syscalls_flash_end__ )\r
285                                                                                         {\r
286                                                                                                 __asm volatile\r
287                                                                                                 (\r
288                                                                                                         "       mrs r1, control         \n" /* Obtain current control value. */\r
289                                                                                                         "       bic r1, #1                      \n" /* Set privilege bit. */\r
290                                                                                                         "       msr control, r1         \n" /* Write back new control value. */\r
291                                                                                                         ::: "r1", "memory"\r
292                                                                                                 );\r
293                                                                                         }\r
294                                                                                         break;\r
295         #else\r
296                 case portSVC_RAISE_PRIVILEGE    :       __asm volatile\r
297                                                                                         (\r
298                                                                                                 "       mrs r1, control         \n" /* Obtain current control value. */\r
299                                                                                                 "       bic r1, #1                      \n" /* Set privilege bit. */\r
300                                                                                                 "       msr control, r1         \n" /* Write back new control value. */\r
301                                                                                                 ::: "r1", "memory"\r
302                                                                                         );\r
303                                                                                         break;\r
304         #endif /* #if( configENFORCE_SYSTEM_CALLS_FROM_KERNEL_ONLY == 1 ) */\r
305 \r
306                 default                                                 :       /* Unknown SVC call. */\r
307                                                                                         break;\r
308         }\r
309 }\r
310 /*-----------------------------------------------------------*/\r
311 \r
312 static void prvRestoreContextOfFirstTask( void )\r
313 {\r
314         __asm volatile\r
315         (\r
316                 "       ldr r0, =0xE000ED08                             \n" /* Use the NVIC offset register to locate the stack. */\r
317                 "       ldr r0, [r0]                                    \n"\r
318                 "       ldr r0, [r0]                                    \n"\r
319                 "       msr msp, r0                                             \n" /* Set the msp back to the start of the stack. */\r
320                 "       ldr     r3, pxCurrentTCBConst2          \n" /* Restore the context. */\r
321                 "       ldr r1, [r3]                                    \n"\r
322                 "       ldr r0, [r1]                                    \n" /* The first item in the TCB is the task top of stack. */\r
323                 "       add r1, r1, #4                                  \n" /* Move onto the second item in the TCB... */\r
324                 "                                                                       \n"\r
325                 "       dmb                                                             \n" /* Complete outstanding transfers before disabling MPU. */\r
326                 "       ldr r2, =0xe000ed94                             \n" /* MPU_CTRL register. */\r
327                 "       ldr r3, [r2]                                    \n" /* Read the value of MPU_CTRL. */\r
328                 "       bic r3, #1                                              \n" /* r3 = r3 & ~1 i.e. Clear the bit 0 in r3. */\r
329                 "       str r3, [r2]                                    \n" /* Disable MPU. */\r
330                 "                                                                       \n"\r
331                 "       ldr r2, =0xe000ed9c                             \n" /* Region Base Address register. */\r
332                 "       ldmia r1!, {r4-r11}                             \n" /* Read 4 sets of MPU registers. */\r
333                 "       stmia r2!, {r4-r11}                             \n" /* Write 4 sets of MPU registers. */\r
334                 "                                                                       \n"\r
335                 "       ldr r2, =0xe000ed94                             \n" /* MPU_CTRL register. */\r
336                 "       ldr r3, [r2]                                    \n" /* Read the value of MPU_CTRL. */\r
337                 "       orr r3, #1                                              \n" /* r3 = r3 | 1 i.e. Set the bit 0 in r3. */\r
338                 "       str r3, [r2]                                    \n" /* Enable MPU. */\r
339                 "       dsb                                                             \n" /* Force memory writes before continuing. */\r
340                 "                                                                       \n"\r
341                 "       ldmia r0!, {r3, r4-r11}                 \n" /* Pop the registers that are not automatically saved on exception entry. */\r
342                 "       msr control, r3                                 \n"\r
343                 "       msr psp, r0                                             \n" /* Restore the task stack pointer. */\r
344                 "       mov r0, #0                                              \n"\r
345                 "       msr     basepri, r0                                     \n"\r
346                 "       ldr r14, =0xfffffffd                    \n" /* Load exec return code. */\r
347                 "       bx r14                                                  \n"\r
348                 "                                                                       \n"\r
349                 "       .align 4                                                \n"\r
350                 "pxCurrentTCBConst2: .word pxCurrentTCB \n"\r
351         );\r
352 }\r
353 /*-----------------------------------------------------------*/\r
354 \r
355 /*\r
356  * See header file for description.\r
357  */\r
358 BaseType_t xPortStartScheduler( void )\r
359 {\r
360         /* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0.  See\r
361          * http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */\r
362         configASSERT( ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) );\r
363 \r
364         #if( configASSERT_DEFINED == 1 )\r
365         {\r
366                 volatile uint32_t ulOriginalPriority;\r
367                 volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER );\r
368                 volatile uint8_t ucMaxPriorityValue;\r
369 \r
370                 /* Determine the maximum priority from which ISR safe FreeRTOS API\r
371                  * functions can be called.  ISR safe functions are those that end in\r
372                  * "FromISR".  FreeRTOS maintains separate thread and ISR API functions\r
373                  * to ensure interrupt entry is as fast and simple as possible.\r
374 \r
375                  * Save the interrupt priority value that is about to be clobbered. */\r
376                 ulOriginalPriority = *pucFirstUserPriorityRegister;\r
377 \r
378                 /* Determine the number of priority bits available.  First write to all\r
379                  * possible bits. */\r
380                 *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;\r
381 \r
382                 /* Read the value back to see how many bits stuck. */\r
383                 ucMaxPriorityValue = *pucFirstUserPriorityRegister;\r
384 \r
385                 /* Use the same mask on the maximum system call priority. */\r
386                 ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;\r
387 \r
388                 /* Calculate the maximum acceptable priority group value for the number\r
389                  * of bits read back. */\r
390                 ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;\r
391                 while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )\r
392                 {\r
393                         ulMaxPRIGROUPValue--;\r
394                         ucMaxPriorityValue <<= ( uint8_t ) 0x01;\r
395                 }\r
396 \r
397                 #ifdef __NVIC_PRIO_BITS\r
398                 {\r
399                         /* Check the CMSIS configuration that defines the number of\r
400                          * priority bits matches the number of priority bits actually queried\r
401                          * from the hardware. */\r
402                         configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS );\r
403                 }\r
404                 #endif\r
405 \r
406                 #ifdef configPRIO_BITS\r
407                 {\r
408                         /* Check the FreeRTOS configuration that defines the number of\r
409                          * priority bits matches the number of priority bits actually queried\r
410                          * from the hardware. */\r
411                         configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS );\r
412                 }\r
413                 #endif\r
414 \r
415                 /* Shift the priority group value back to its position within the AIRCR\r
416                  * register. */\r
417                 ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;\r
418                 ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;\r
419 \r
420                 /* Restore the clobbered interrupt priority register to its original\r
421                  * value. */\r
422                 *pucFirstUserPriorityRegister = ulOriginalPriority;\r
423         }\r
424         #endif /* conifgASSERT_DEFINED */\r
425 \r
426         /* Make PendSV and SysTick the same priority as the kernel, and the SVC\r
427          * handler higher priority so it can be used to exit a critical section (where\r
428          * lower priorities are masked). */\r
429         portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;\r
430         portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;\r
431 \r
432         /* Configure the regions in the MPU that are common to all tasks. */\r
433         prvSetupMPU();\r
434 \r
435         /* Start the timer that generates the tick ISR.  Interrupts are disabled\r
436          * here already. */\r
437         vPortSetupTimerInterrupt();\r
438 \r
439         /* Initialise the critical nesting count ready for the first task. */\r
440         uxCriticalNesting = 0;\r
441 \r
442         /* Start the first task. */\r
443         __asm volatile(\r
444                                         " ldr r0, =0xE000ED08   \n" /* Use the NVIC offset register to locate the stack. */\r
445                                         " ldr r0, [r0]                  \n"\r
446                                         " ldr r0, [r0]                  \n"\r
447                                         " msr msp, r0                   \n" /* Set the msp back to the start of the stack. */\r
448                                         " cpsie i                               \n" /* Globally enable interrupts. */\r
449                                         " cpsie f                               \n"\r
450                                         " dsb                                   \n"\r
451                                         " isb                                   \n"\r
452                                         " svc %0                                \n" /* System call to start first task. */\r
453                                         " nop                                   \n"\r
454                                         :: "i" (portSVC_START_SCHEDULER) : "memory" );\r
455 \r
456         /* Should not get here! */\r
457         return 0;\r
458 }\r
459 /*-----------------------------------------------------------*/\r
460 \r
461 void vPortEndScheduler( void )\r
462 {\r
463         /* Not implemented in ports where there is nothing to return to.\r
464          * Artificially force an assert. */\r
465         configASSERT( uxCriticalNesting == 1000UL );\r
466 }\r
467 /*-----------------------------------------------------------*/\r
468 \r
469 void vPortEnterCritical( void )\r
470 {\r
471 BaseType_t xRunningPrivileged = xPortRaisePrivilege();\r
472 \r
473         portDISABLE_INTERRUPTS();\r
474         uxCriticalNesting++;\r
475 \r
476         vPortResetPrivilege( xRunningPrivileged );\r
477 }\r
478 /*-----------------------------------------------------------*/\r
479 \r
480 void vPortExitCritical( void )\r
481 {\r
482 BaseType_t xRunningPrivileged = xPortRaisePrivilege();\r
483 \r
484         configASSERT( uxCriticalNesting );\r
485         uxCriticalNesting--;\r
486         if( uxCriticalNesting == 0 )\r
487         {\r
488                 portENABLE_INTERRUPTS();\r
489         }\r
490         vPortResetPrivilege( xRunningPrivileged );\r
491 }\r
492 /*-----------------------------------------------------------*/\r
493 \r
494 void xPortPendSVHandler( void )\r
495 {\r
496         /* This is a naked function. */\r
497 \r
498         __asm volatile\r
499         (\r
500                 "       mrs r0, psp                                                     \n"\r
501                 "                                                                               \n"\r
502                 "       ldr     r3, pxCurrentTCBConst                   \n" /* Get the location of the current TCB. */\r
503                 "       ldr     r2, [r3]                                                \n"\r
504                 "                                                                               \n"\r
505                 "       mrs r1, control                                         \n"\r
506                 "       stmdb r0!, {r1, r4-r11}                         \n" /* Save the remaining registers. */\r
507                 "       str r0, [r2]                                            \n" /* Save the new top of stack into the first member of the TCB. */\r
508                 "                                                                               \n"\r
509                 "       stmdb sp!, {r3, r14}                            \n"\r
510                 "       mov r0, %0                                                      \n"\r
511                 "       msr basepri, r0                                         \n"\r
512                 "       dsb                                                                     \n"\r
513                 "       isb                                                                     \n"\r
514                 "       bl vTaskSwitchContext                           \n"\r
515                 "       mov r0, #0                                                      \n"\r
516                 "       msr basepri, r0                                         \n"\r
517                 "       ldmia sp!, {r3, r14}                            \n"\r
518                 "                                                                               \n"     /* Restore the context. */\r
519                 "       ldr r1, [r3]                                            \n"\r
520                 "       ldr r0, [r1]                                            \n" /* The first item in the TCB is the task top of stack. */\r
521                 "       add r1, r1, #4                                          \n" /* Move onto the second item in the TCB... */\r
522                 "                                                                               \n"\r
523                 "       dmb                                                                     \n" /* Complete outstanding transfers before disabling MPU. */\r
524                 "       ldr r2, =0xe000ed94                                     \n" /* MPU_CTRL register. */\r
525                 "       ldr r3, [r2]                                            \n" /* Read the value of MPU_CTRL. */\r
526                 "       bic r3, #1                                                      \n" /* r3 = r3 & ~1 i.e. Clear the bit 0 in r3. */\r
527                 "       str r3, [r2]                                            \n" /* Disable MPU. */\r
528                 "                                                                               \n"\r
529                 "       ldr r2, =0xe000ed9c                                     \n" /* Region Base Address register. */\r
530                 "       ldmia r1!, {r4-r11}                                     \n" /* Read 4 sets of MPU registers. */\r
531                 "       stmia r2!, {r4-r11}                                     \n" /* Write 4 sets of MPU registers. */\r
532                 "                                                                               \n"\r
533                 "       ldr r2, =0xe000ed94                                     \n" /* MPU_CTRL register. */\r
534                 "       ldr r3, [r2]                                            \n" /* Read the value of MPU_CTRL. */\r
535                 "       orr r3, #1                                                      \n" /* r3 = r3 | 1 i.e. Set the bit 0 in r3. */\r
536                 "       str r3, [r2]                                            \n" /* Enable MPU. */\r
537                 "       dsb                                                                     \n" /* Force memory writes before continuing. */\r
538                 "                                                                               \n"\r
539                 "       ldmia r0!, {r3, r4-r11}                         \n" /* Pop the registers that are not automatically saved on exception entry. */\r
540                 "       msr control, r3                                         \n"\r
541                 "                                                                               \n"\r
542                 "       msr psp, r0                                                     \n"\r
543                 "       bx r14                                                          \n"\r
544                 "                                                                               \n"\r
545                 "       .align 4                                                        \n"\r
546                 "pxCurrentTCBConst: .word pxCurrentTCB  \n"\r
547                 ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY)\r
548         );\r
549 }\r
550 /*-----------------------------------------------------------*/\r
551 \r
552 void xPortSysTickHandler( void )\r
553 {\r
554 uint32_t ulDummy;\r
555 \r
556         ulDummy = portSET_INTERRUPT_MASK_FROM_ISR();\r
557         {\r
558                 /* Increment the RTOS tick. */\r
559                 if( xTaskIncrementTick() != pdFALSE )\r
560                 {\r
561                         /* Pend a context switch. */\r
562                         portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;\r
563                 }\r
564         }\r
565         portCLEAR_INTERRUPT_MASK_FROM_ISR( ulDummy );\r
566 }\r
567 /*-----------------------------------------------------------*/\r
568 \r
569 /*\r
570  * Setup the systick timer to generate the tick interrupts at the required\r
571  * frequency.\r
572  */\r
573 __attribute__(( weak )) void vPortSetupTimerInterrupt( void )\r
574 {\r
575         /* Stop and clear the SysTick. */\r
576         portNVIC_SYSTICK_CTRL_REG = 0UL;\r
577         portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;\r
578 \r
579         /* Configure SysTick to interrupt at the requested rate. */\r
580         portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;\r
581         portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK | portNVIC_SYSTICK_INT | portNVIC_SYSTICK_ENABLE );\r
582 }\r
583 /*-----------------------------------------------------------*/\r
584 \r
585 static void prvSetupMPU( void )\r
586 {\r
587 extern uint32_t __privileged_functions_end__[];\r
588 extern uint32_t __FLASH_segment_start__[];\r
589 extern uint32_t __FLASH_segment_end__[];\r
590 extern uint32_t __privileged_data_start__[];\r
591 extern uint32_t __privileged_data_end__[];\r
592 \r
593         /* Check the expected MPU is present. */\r
594         if( portMPU_TYPE_REG == portEXPECTED_MPU_TYPE_VALUE )\r
595         {\r
596                 /* First setup the entire flash for unprivileged read only access. */\r
597                 portMPU_REGION_BASE_ADDRESS_REG =       ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */\r
598                                                                                         ( portMPU_REGION_VALID ) |\r
599                                                                                         ( portUNPRIVILEGED_FLASH_REGION );\r
600 \r
601                 portMPU_REGION_ATTRIBUTE_REG =  ( portMPU_REGION_READ_ONLY ) |\r
602                                                                                 ( portMPU_REGION_CACHEABLE_BUFFERABLE ) |\r
603                                                                                 ( prvGetMPURegionSizeSetting( ( uint32_t ) __FLASH_segment_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) |\r
604                                                                                 ( portMPU_REGION_ENABLE );\r
605 \r
606                 /* Setup the first 16K for privileged only access (even though less\r
607                  * than 10K is actually being used).  This is where the kernel code is\r
608                  * placed. */\r
609                 portMPU_REGION_BASE_ADDRESS_REG =       ( ( uint32_t ) __FLASH_segment_start__ ) | /* Base address. */\r
610                                                                                         ( portMPU_REGION_VALID ) |\r
611                                                                                         ( portPRIVILEGED_FLASH_REGION );\r
612 \r
613                 portMPU_REGION_ATTRIBUTE_REG =  ( portMPU_REGION_PRIVILEGED_READ_ONLY ) |\r
614                                                                                 ( portMPU_REGION_CACHEABLE_BUFFERABLE ) |\r
615                                                                                 ( prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_functions_end__ - ( uint32_t ) __FLASH_segment_start__ ) ) |\r
616                                                                                 ( portMPU_REGION_ENABLE );\r
617 \r
618                 /* Setup the privileged data RAM region.  This is where the kernel data\r
619                  * is placed. */\r
620                 portMPU_REGION_BASE_ADDRESS_REG =       ( ( uint32_t ) __privileged_data_start__ ) | /* Base address. */\r
621                                                                                         ( portMPU_REGION_VALID ) |\r
622                                                                                         ( portPRIVILEGED_RAM_REGION );\r
623 \r
624                 portMPU_REGION_ATTRIBUTE_REG =  ( portMPU_REGION_PRIVILEGED_READ_WRITE ) |\r
625                                                                                 ( portMPU_REGION_CACHEABLE_BUFFERABLE ) |\r
626                                                                                 prvGetMPURegionSizeSetting( ( uint32_t ) __privileged_data_end__ - ( uint32_t ) __privileged_data_start__ ) |\r
627                                                                                 ( portMPU_REGION_ENABLE );\r
628 \r
629                 /* By default allow everything to access the general peripherals.  The\r
630                  * system peripherals and registers are protected. */\r
631                 portMPU_REGION_BASE_ADDRESS_REG =       ( portPERIPHERALS_START_ADDRESS ) |\r
632                                                                                         ( portMPU_REGION_VALID ) |\r
633                                                                                         ( portGENERAL_PERIPHERALS_REGION );\r
634 \r
635                 portMPU_REGION_ATTRIBUTE_REG =  ( portMPU_REGION_READ_WRITE | portMPU_REGION_EXECUTE_NEVER ) |\r
636                                                                                 ( prvGetMPURegionSizeSetting( portPERIPHERALS_END_ADDRESS - portPERIPHERALS_START_ADDRESS ) ) |\r
637                                                                                 ( portMPU_REGION_ENABLE );\r
638 \r
639                 /* Enable the memory fault exception. */\r
640                 portNVIC_SYS_CTRL_STATE_REG |= portNVIC_MEM_FAULT_ENABLE;\r
641 \r
642                 /* Enable the MPU with the background region configured. */\r
643                 portMPU_CTRL_REG |= ( portMPU_ENABLE | portMPU_BACKGROUND_ENABLE );\r
644         }\r
645 }\r
646 /*-----------------------------------------------------------*/\r
647 \r
648 static uint32_t prvGetMPURegionSizeSetting( uint32_t ulActualSizeInBytes )\r
649 {\r
650 uint32_t ulRegionSize, ulReturnValue = 4;\r
651 \r
652         /* 32 is the smallest region size, 31 is the largest valid value for\r
653          * ulReturnValue. */\r
654         for( ulRegionSize = 32UL; ulReturnValue < 31UL; ( ulRegionSize <<= 1UL ) )\r
655         {\r
656                 if( ulActualSizeInBytes <= ulRegionSize )\r
657                 {\r
658                         break;\r
659                 }\r
660                 else\r
661                 {\r
662                         ulReturnValue++;\r
663                 }\r
664         }\r
665 \r
666         /* Shift the code by one before returning so it can be written directly\r
667          * into the the correct bit position of the attribute register. */\r
668         return ( ulReturnValue << 1UL );\r
669 }\r
670 /*-----------------------------------------------------------*/\r
671 \r
672 BaseType_t xIsPrivileged( void ) /* __attribute__ (( naked )) */\r
673 {\r
674         __asm volatile\r
675         (\r
676         "       mrs r0, control                                                 \n" /* r0 = CONTROL. */\r
677         "       tst r0, #1                                                              \n" /* Perform r0 & 1 (bitwise AND) and update the conditions flag. */\r
678         "       ite ne                                                                  \n"\r
679         "       movne r0, #0                                                    \n" /* CONTROL[0]!=0. Return false to indicate that the processor is not privileged. */\r
680         "       moveq r0, #1                                                    \n" /* CONTROL[0]==0. Return true to indicate that the processor is privileged. */\r
681         "       bx lr                                                                   \n" /* Return. */\r
682         "                                                                                       \n"\r
683         "       .align 4                                                                \n"\r
684         ::: "r0", "memory"\r
685         );\r
686 }\r
687 /*-----------------------------------------------------------*/\r
688 \r
689 void vResetPrivilege( void ) /* __attribute__ (( naked )) */\r
690 {\r
691         __asm volatile\r
692         (\r
693         "       mrs r0, control                                                 \n" /* r0 = CONTROL. */\r
694         "       orr r0, #1                                                              \n" /* r0 = r0 | 1. */\r
695         "       msr control, r0                                                 \n" /* CONTROL = r0. */\r
696         "       bx lr                                                                   \n" /* Return to the caller. */\r
697         :::"r0", "memory"\r
698         );\r
699 }\r
700 /*-----------------------------------------------------------*/\r
701 \r
702 void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, StackType_t *pxBottomOfStack, uint32_t ulStackDepth )\r
703 {\r
704 extern uint32_t __SRAM_segment_start__[];\r
705 extern uint32_t __SRAM_segment_end__[];\r
706 extern uint32_t __privileged_data_start__[];\r
707 extern uint32_t __privileged_data_end__[];\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 #if( configASSERT_DEFINED == 1 )\r
799 \r
800         void vPortValidateInterruptPriority( void )\r
801         {\r
802         uint32_t ulCurrentInterrupt;\r
803         uint8_t ucCurrentPriority;\r
804 \r
805                 /* Obtain the number of the currently executing interrupt. */\r
806                 __asm volatile( "mrs %0, ipsr" : "=r"( ulCurrentInterrupt ) :: "memory" );\r
807 \r
808                 /* Is the interrupt number a user defined interrupt? */\r
809                 if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER )\r
810                 {\r
811                         /* Look up the interrupt's priority. */\r
812                         ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ];\r
813 \r
814                         /* The following assertion will fail if a service routine (ISR) for\r
815                          * an interrupt that has been assigned a priority above\r
816                          * configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API\r
817                          * function.  ISR safe FreeRTOS API functions must *only* be called\r
818                          * from interrupts that have been assigned a priority at or below\r
819                          * configMAX_SYSCALL_INTERRUPT_PRIORITY.\r
820 \r
821                          * Numerically low interrupt priority numbers represent logically high\r
822                          * interrupt priorities, therefore the priority of the interrupt must\r
823                          * be set to a value equal to or numerically *higher* than\r
824                          * configMAX_SYSCALL_INTERRUPT_PRIORITY.\r
825 \r
826                          * Interrupts that      use the FreeRTOS API must not be left at their\r
827                          * default priority of  zero as that is the highest possible priority,\r
828                          * which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY,\r
829                          * and  therefore also guaranteed to be invalid.\r
830 \r
831                          * FreeRTOS maintains separate thread and ISR API functions to ensure\r
832                          * interrupt entry is as fast and simple as possible.\r
833 \r
834                          * The following links provide detailed information:\r
835                          * http://www.freertos.org/RTOS-Cortex-M3-M4.html\r
836                          * http://www.freertos.org/FAQHelp.html */\r
837                         configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );\r
838                 }\r
839 \r
840                 /* Priority grouping:  The interrupt controller (NVIC) allows the bits\r
841                  * that define each interrupt's priority to be split between bits that\r
842                  * define the interrupt's pre-emption priority bits and bits that define\r
843                  * the interrupt's sub-priority.  For simplicity all bits must be defined\r
844                  * to be pre-emption priority bits.  The following assertion will fail if\r
845                  * this is not the case (if some bits represent a sub-priority).\r
846 \r
847                  * If the application only uses CMSIS libraries for interrupt\r
848                  * configuration then the correct setting can be achieved on all Cortex-M\r
849                  * devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the\r
850                  * scheduler.  Note however that some vendor specific peripheral libraries\r
851                  * assume a non-zero priority group setting, in which cases using a value\r
852                  * of zero will result in unpredicable behaviour. */\r
853                 configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue );\r
854         }\r
855 \r
856 #endif /* configASSERT_DEFINED */\r
857 /*-----------------------------------------------------------*/\r