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