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