]> begriffs open source - freertos/blob - portable/RVDS/ARM_CM7/r0p1/port.c
[AUTO][RELEASE]: Bump file header version to "10.4.2"
[freertos] / portable / RVDS / ARM_CM7 / r0p1 / port.c
1 /*\r
2  * FreeRTOS Kernel V10.4.2\r
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21  *\r
22  * https://www.FreeRTOS.org\r
23  * https://github.com/FreeRTOS\r
24  *\r
25  */\r
26 \r
27 /*-----------------------------------------------------------\r
28 * Implementation of functions defined in portable.h for the ARM CM7 port.\r
29 *----------------------------------------------------------*/\r
30 \r
31 /* Scheduler includes. */\r
32 #include "FreeRTOS.h"\r
33 #include "task.h"\r
34 \r
35 #ifndef __TARGET_FPU_VFP\r
36     #error This port can only be used when the project options are configured to enable hardware floating point support.\r
37 #endif\r
38 \r
39 #if configMAX_SYSCALL_INTERRUPT_PRIORITY == 0\r
40     #error configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to 0.  See http: /*www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */\r
41 #endif\r
42 \r
43 #ifndef configSYSTICK_CLOCK_HZ\r
44     #define configSYSTICK_CLOCK_HZ      configCPU_CLOCK_HZ\r
45     /* Ensure the SysTick is clocked at the same frequency as the core. */\r
46     #define portNVIC_SYSTICK_CLK_BIT    ( 1UL << 2UL )\r
47 #else\r
48 \r
49 /* The way the SysTick is clocked is not modified in case it is not the same\r
50  * as the core. */\r
51     #define portNVIC_SYSTICK_CLK_BIT    ( 0 )\r
52 #endif\r
53 \r
54 /* The __weak attribute does not work as you might expect with the Keil tools\r
55  * so the configOVERRIDE_DEFAULT_TICK_CONFIGURATION constant must be set to 1 if\r
56  * the application writer wants to provide their own implementation of\r
57  * vPortSetupTimerInterrupt().  Ensure configOVERRIDE_DEFAULT_TICK_CONFIGURATION\r
58  * is defined. */\r
59 #ifndef configOVERRIDE_DEFAULT_TICK_CONFIGURATION\r
60     #define configOVERRIDE_DEFAULT_TICK_CONFIGURATION    0\r
61 #endif\r
62 \r
63 /* Constants required to manipulate the core.  Registers first... */\r
64 #define portNVIC_SYSTICK_CTRL_REG             ( *( ( volatile uint32_t * ) 0xe000e010 ) )\r
65 #define portNVIC_SYSTICK_LOAD_REG             ( *( ( volatile uint32_t * ) 0xe000e014 ) )\r
66 #define portNVIC_SYSTICK_CURRENT_VALUE_REG    ( *( ( volatile uint32_t * ) 0xe000e018 ) )\r
67 #define portNVIC_SHPR3_REG                    ( *( ( volatile uint32_t * ) 0xe000ed20 ) )\r
68 /* ...then bits in the registers. */\r
69 #define portNVIC_SYSTICK_INT_BIT              ( 1UL << 1UL )\r
70 #define portNVIC_SYSTICK_ENABLE_BIT           ( 1UL << 0UL )\r
71 #define portNVIC_SYSTICK_COUNT_FLAG_BIT       ( 1UL << 16UL )\r
72 #define portNVIC_PENDSVCLEAR_BIT              ( 1UL << 27UL )\r
73 #define portNVIC_PEND_SYSTICK_CLEAR_BIT       ( 1UL << 25UL )\r
74 \r
75 #define portNVIC_PENDSV_PRI                   ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )\r
76 #define portNVIC_SYSTICK_PRI                  ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )\r
77 \r
78 /* Constants required to check the validity of an interrupt priority. */\r
79 #define portFIRST_USER_INTERRUPT_NUMBER       ( 16 )\r
80 #define portNVIC_IP_REGISTERS_OFFSET_16       ( 0xE000E3F0 )\r
81 #define portAIRCR_REG                         ( *( ( volatile uint32_t * ) 0xE000ED0C ) )\r
82 #define portMAX_8_BIT_VALUE                   ( ( uint8_t ) 0xff )\r
83 #define portTOP_BIT_OF_BYTE                   ( ( uint8_t ) 0x80 )\r
84 #define portMAX_PRIGROUP_BITS                 ( ( uint8_t ) 7 )\r
85 #define portPRIORITY_GROUP_MASK               ( 0x07UL << 8UL )\r
86 #define portPRIGROUP_SHIFT                    ( 8UL )\r
87 \r
88 /* Masks off all bits but the VECTACTIVE bits in the ICSR register. */\r
89 #define portVECTACTIVE_MASK                   ( 0xFFUL )\r
90 \r
91 /* Constants required to manipulate the VFP. */\r
92 #define portFPCCR                             ( ( volatile uint32_t * ) 0xe000ef34 ) /* Floating point context control register. */\r
93 #define portASPEN_AND_LSPEN_BITS              ( 0x3UL << 30UL )\r
94 \r
95 /* Constants required to set up the initial stack. */\r
96 #define portINITIAL_XPSR                      ( 0x01000000 )\r
97 #define portINITIAL_EXC_RETURN                ( 0xfffffffd )\r
98 \r
99 /* The systick is a 24-bit counter. */\r
100 #define portMAX_24_BIT_NUMBER                 ( 0xffffffUL )\r
101 \r
102 /* A fiddle factor to estimate the number of SysTick counts that would have\r
103  * occurred while the SysTick counter is stopped during tickless idle\r
104  * calculations. */\r
105 #define portMISSED_COUNTS_FACTOR              ( 45UL )\r
106 \r
107 /* For strict compliance with the Cortex-M spec the task start address should\r
108  * have bit-0 clear, as it is loaded into the PC on exit from an ISR. */\r
109 #define portSTART_ADDRESS_MASK                ( ( StackType_t ) 0xfffffffeUL )\r
110 \r
111 /*\r
112  * Setup the timer to generate the tick interrupts.  The implementation in this\r
113  * file is weak to allow application writers to change the timer used to\r
114  * generate the tick interrupt.\r
115  */\r
116 void vPortSetupTimerInterrupt( void );\r
117 \r
118 /*\r
119  * Exception handlers.\r
120  */\r
121 void xPortPendSVHandler( void );\r
122 void xPortSysTickHandler( void );\r
123 void vPortSVCHandler( void );\r
124 \r
125 /*\r
126  * Start first task is a separate function so it can be tested in isolation.\r
127  */\r
128 static void prvStartFirstTask( void );\r
129 \r
130 /*\r
131  * Functions defined in portasm.s to enable the VFP.\r
132  */\r
133 static void prvEnableVFP( void );\r
134 \r
135 /*\r
136  * Used to catch tasks that attempt to return from their implementing function.\r
137  */\r
138 static void prvTaskExitError( void );\r
139 \r
140 /*-----------------------------------------------------------*/\r
141 \r
142 /* Each task maintains its own interrupt status in the critical nesting\r
143  * variable. */\r
144 static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;\r
145 \r
146 /*\r
147  * The number of SysTick increments that make up one tick period.\r
148  */\r
149 #if ( configUSE_TICKLESS_IDLE == 1 )\r
150     static uint32_t ulTimerCountsForOneTick = 0;\r
151 #endif /* configUSE_TICKLESS_IDLE */\r
152 \r
153 /*\r
154  * The maximum number of tick periods that can be suppressed is limited by the\r
155  * 24 bit resolution of the SysTick timer.\r
156  */\r
157 #if ( configUSE_TICKLESS_IDLE == 1 )\r
158     static uint32_t xMaximumPossibleSuppressedTicks = 0;\r
159 #endif /* configUSE_TICKLESS_IDLE */\r
160 \r
161 /*\r
162  * Compensate for the CPU cycles that pass while the SysTick is stopped (low\r
163  * power functionality only.\r
164  */\r
165 #if ( configUSE_TICKLESS_IDLE == 1 )\r
166     static uint32_t ulStoppedTimerCompensation = 0;\r
167 #endif /* configUSE_TICKLESS_IDLE */\r
168 \r
169 /*\r
170  * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure\r
171  * FreeRTOS API functions are not called from interrupts that have been assigned\r
172  * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY.\r
173  */\r
174 #if ( configASSERT_DEFINED == 1 )\r
175     static uint8_t ucMaxSysCallPriority = 0;\r
176     static uint32_t ulMaxPRIGROUPValue = 0;\r
177     static const volatile uint8_t * const pcInterruptPriorityRegisters = ( uint8_t * ) portNVIC_IP_REGISTERS_OFFSET_16;\r
178 #endif /* configASSERT_DEFINED */\r
179 \r
180 /*-----------------------------------------------------------*/\r
181 \r
182 /*\r
183  * See header file for description.\r
184  */\r
185 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,\r
186                                      TaskFunction_t pxCode,\r
187                                      void * pvParameters )\r
188 {\r
189     /* Simulate the stack frame as it would be created by a context switch\r
190      * interrupt. */\r
191 \r
192     /* Offset added to account for the way the MCU uses the stack on entry/exit\r
193      * of interrupts, and to ensure alignment. */\r
194     pxTopOfStack--;\r
195 \r
196     *pxTopOfStack = portINITIAL_XPSR;                                    /* xPSR */\r
197     pxTopOfStack--;\r
198     *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */\r
199     pxTopOfStack--;\r
200     *pxTopOfStack = ( StackType_t ) prvTaskExitError;                    /* LR */\r
201 \r
202     /* Save code space by skipping register initialisation. */\r
203     pxTopOfStack -= 5;                            /* R12, R3, R2 and R1. */\r
204     *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */\r
205 \r
206     /* A save method is being used that requires each task to maintain its\r
207      * own exec return value. */\r
208     pxTopOfStack--;\r
209     *pxTopOfStack = portINITIAL_EXC_RETURN;\r
210 \r
211     pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */\r
212 \r
213     return pxTopOfStack;\r
214 }\r
215 /*-----------------------------------------------------------*/\r
216 \r
217 static void prvTaskExitError( void )\r
218 {\r
219     /* A function that implements a task must not exit or attempt to return to\r
220      * its caller as there is nothing to return to.  If a task wants to exit it\r
221      * should instead call vTaskDelete( NULL ).\r
222      *\r
223      * Artificially force an assert() to be triggered if configASSERT() is\r
224      * defined, then stop here so application writers can catch the error. */\r
225     configASSERT( uxCriticalNesting == ~0UL );\r
226     portDISABLE_INTERRUPTS();\r
227 \r
228     for( ; ; )\r
229     {\r
230     }\r
231 }\r
232 /*-----------------------------------------------------------*/\r
233 \r
234 __asm void vPortSVCHandler( void )\r
235 {\r
236     PRESERVE8\r
237 \r
238 /* *INDENT-OFF* */\r
239     /* Get the location of the current TCB. */\r
240     ldr r3, =pxCurrentTCB\r
241     ldr r1, [ r3 ]\r
242     ldr r0, [ r1 ]\r
243     /* Pop the core registers. */\r
244     ldmia r0 !, { r4 - r11, r14 }\r
245     msr psp, r0\r
246     isb\r
247     mov r0, #0\r
248     msr basepri, r0\r
249     bx r14\r
250 /* *INDENT-ON* */\r
251 }\r
252 /*-----------------------------------------------------------*/\r
253 \r
254 __asm void prvStartFirstTask( void )\r
255 {\r
256 /* *INDENT-OFF* */\r
257     PRESERVE8\r
258 \r
259     /* Use the NVIC offset register to locate the stack. */\r
260     ldr r0, =0xE000ED08\r
261     ldr r0, [ r0 ]\r
262     ldr r0, [ r0 ]\r
263     /* Set the msp back to the start of the stack. */\r
264     msr msp, r0\r
265 \r
266     /* Clear the bit that indicates the FPU is in use in case the FPU was used\r
267      * before the scheduler was started - which would otherwise result in the\r
268      * unnecessary leaving of space in the SVC stack for lazy saving of FPU\r
269      * registers. */\r
270     mov r0, #0\r
271     msr control, r0\r
272     /* Globally enable interrupts. */\r
273     cpsie i\r
274     cpsie f\r
275     dsb\r
276     isb\r
277     /* Call SVC to start the first task. */\r
278     svc 0\r
279     nop\r
280     nop\r
281 /* *INDENT-ON* */\r
282 }\r
283 /*-----------------------------------------------------------*/\r
284 \r
285 __asm void prvEnableVFP( void )\r
286 {\r
287 /* *INDENT-OFF* */\r
288     PRESERVE8\r
289 \r
290     /* The FPU enable bits are in the CPACR. */\r
291     ldr.w r0, =0xE000ED88\r
292     ldr r1, [ r0 ]\r
293 \r
294     /* Enable CP10 and CP11 coprocessors, then save back. */\r
295     orr r1, r1, #( 0xf << 20 )\r
296     str r1, [ r0 ]\r
297     bx r14\r
298     nop\r
299 /* *INDENT-ON* */\r
300 }\r
301 /*-----------------------------------------------------------*/\r
302 \r
303 /*\r
304  * See header file for description.\r
305  */\r
306 BaseType_t xPortStartScheduler( void )\r
307 {\r
308     #if ( configASSERT_DEFINED == 1 )\r
309         {\r
310             volatile uint32_t ulOriginalPriority;\r
311             volatile uint8_t * const pucFirstUserPriorityRegister = ( uint8_t * ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER );\r
312             volatile uint8_t ucMaxPriorityValue;\r
313 \r
314             /* Determine the maximum priority from which ISR safe FreeRTOS API\r
315              * functions can be called.  ISR safe functions are those that end in\r
316              * "FromISR".  FreeRTOS maintains separate thread and ISR API functions to\r
317              * ensure interrupt entry is as fast and simple as possible.\r
318              *\r
319              * Save the interrupt priority value that is about to be clobbered. */\r
320             ulOriginalPriority = *pucFirstUserPriorityRegister;\r
321 \r
322             /* Determine the number of priority bits available.  First write to all\r
323              * possible bits. */\r
324             *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE;\r
325 \r
326             /* Read the value back to see how many bits stuck. */\r
327             ucMaxPriorityValue = *pucFirstUserPriorityRegister;\r
328 \r
329             /* The kernel interrupt priority should be set to the lowest\r
330              * priority. */\r
331             configASSERT( ucMaxPriorityValue == ( configKERNEL_INTERRUPT_PRIORITY & ucMaxPriorityValue ) );\r
332 \r
333             /* Use the same mask on the maximum system call priority. */\r
334             ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;\r
335 \r
336             /* Calculate the maximum acceptable priority group value for the number\r
337              * of bits read back. */\r
338             ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS;\r
339 \r
340             while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE )\r
341             {\r
342                 ulMaxPRIGROUPValue--;\r
343                 ucMaxPriorityValue <<= ( uint8_t ) 0x01;\r
344             }\r
345 \r
346             #ifdef __NVIC_PRIO_BITS\r
347                 {\r
348                     /* Check the CMSIS configuration that defines the number of\r
349                      * priority bits matches the number of priority bits actually queried\r
350                      * from the hardware. */\r
351                     configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == __NVIC_PRIO_BITS );\r
352                 }\r
353             #endif\r
354 \r
355             #ifdef configPRIO_BITS\r
356                 {\r
357                     /* Check the FreeRTOS configuration that defines the number of\r
358                      * priority bits matches the number of priority bits actually queried\r
359                      * from the hardware. */\r
360                     configASSERT( ( portMAX_PRIGROUP_BITS - ulMaxPRIGROUPValue ) == configPRIO_BITS );\r
361                 }\r
362             #endif\r
363 \r
364             /* Shift the priority group value back to its position within the AIRCR\r
365              * register. */\r
366             ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT;\r
367             ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK;\r
368 \r
369             /* Restore the clobbered interrupt priority register to its original\r
370              * value. */\r
371             *pucFirstUserPriorityRegister = ulOriginalPriority;\r
372         }\r
373     #endif /* conifgASSERT_DEFINED */\r
374 \r
375     /* Make PendSV and SysTick the lowest priority interrupts. */\r
376     portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI;\r
377 \r
378     portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI;\r
379 \r
380     /* Start the timer that generates the tick ISR.  Interrupts are disabled\r
381      * here already. */\r
382     vPortSetupTimerInterrupt();\r
383 \r
384     /* Initialise the critical nesting count ready for the first task. */\r
385     uxCriticalNesting = 0;\r
386 \r
387     /* Ensure the VFP is enabled - it should be anyway. */\r
388     prvEnableVFP();\r
389 \r
390     /* Lazy save always. */\r
391     *( portFPCCR ) |= portASPEN_AND_LSPEN_BITS;\r
392 \r
393     /* Start the first task. */\r
394     prvStartFirstTask();\r
395 \r
396     /* Should not get here! */\r
397     return 0;\r
398 }\r
399 /*-----------------------------------------------------------*/\r
400 \r
401 void vPortEndScheduler( void )\r
402 {\r
403     /* Not implemented in ports where there is nothing to return to.\r
404      * Artificially force an assert. */\r
405     configASSERT( uxCriticalNesting == 1000UL );\r
406 }\r
407 /*-----------------------------------------------------------*/\r
408 \r
409 void vPortEnterCritical( void )\r
410 {\r
411     portDISABLE_INTERRUPTS();\r
412     uxCriticalNesting++;\r
413 \r
414     /* This is not the interrupt safe version of the enter critical function so\r
415      * assert() if it is being called from an interrupt context.  Only API\r
416      * functions that end in "FromISR" can be used in an interrupt.  Only assert if\r
417      * the critical nesting count is 1 to protect against recursive calls if the\r
418      * assert function also uses a critical section. */\r
419     if( uxCriticalNesting == 1 )\r
420     {\r
421         configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );\r
422     }\r
423 }\r
424 /*-----------------------------------------------------------*/\r
425 \r
426 void vPortExitCritical( void )\r
427 {\r
428     configASSERT( uxCriticalNesting );\r
429     uxCriticalNesting--;\r
430 \r
431     if( uxCriticalNesting == 0 )\r
432     {\r
433         portENABLE_INTERRUPTS();\r
434     }\r
435 }\r
436 /*-----------------------------------------------------------*/\r
437 \r
438 __asm void xPortPendSVHandler( void )\r
439 {\r
440     extern uxCriticalNesting;\r
441     extern pxCurrentTCB;\r
442     extern vTaskSwitchContext;\r
443 \r
444 /* *INDENT-OFF* */\r
445     PRESERVE8\r
446 \r
447     mrs r0, psp\r
448     isb\r
449     /* Get the location of the current TCB. */\r
450     ldr r3, =pxCurrentTCB\r
451     ldr r2, [ r3 ]\r
452 \r
453     /* Is the task using the FPU context?  If so, push high vfp registers. */\r
454     tst r14, #0x10\r
455     it eq\r
456     vstmdbeq r0 !, { s16 - s31 }\r
457 \r
458     /* Save the core registers. */\r
459     stmdb r0 !, { r4 - r11, r14 }\r
460 \r
461     /* Save the new top of stack into the first member of the TCB. */\r
462     str r0, [ r2 ]\r
463 \r
464     stmdb sp !, { r0, r3 }\r
465     mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY\r
466     cpsid i\r
467     msr basepri, r0\r
468     dsb\r
469     isb\r
470     cpsie i\r
471     bl vTaskSwitchContext\r
472     mov r0, #0\r
473     msr basepri, r0\r
474     ldmia sp !, { r0, r3 }\r
475 \r
476     /* The first item in pxCurrentTCB is the task top of stack. */\r
477     ldr r1, [ r3 ]\r
478     ldr r0, [ r1 ]\r
479 \r
480     /* Pop the core registers. */\r
481     ldmia r0 !, { r4 - r11, r14 }\r
482 \r
483     /* Is the task using the FPU context?  If so, pop the high vfp registers\r
484      * too. */\r
485     tst r14, #0x10\r
486     it eq\r
487     vldmiaeq r0 !, { s16 - s31 }\r
488 \r
489     msr psp, r0\r
490     isb\r
491     #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata */\r
492         #if WORKAROUND_PMU_CM001 == 1\r
493             push { r14 }\r
494             pop { pc }\r
495             nop\r
496         #endif\r
497     #endif\r
498 \r
499     bx r14\r
500 /* *INDENT-ON* */\r
501 }\r
502 /*-----------------------------------------------------------*/\r
503 \r
504 void xPortSysTickHandler( void )\r
505 {\r
506     /* The SysTick runs at the lowest interrupt priority, so when this interrupt\r
507      * executes all interrupts must be unmasked.  There is therefore no need to\r
508      * save and then restore the interrupt mask value as its value is already\r
509      * known - therefore the slightly faster vPortRaiseBASEPRI() function is used\r
510      * in place of portSET_INTERRUPT_MASK_FROM_ISR(). */\r
511     vPortRaiseBASEPRI();\r
512     {\r
513         /* Increment the RTOS tick. */\r
514         if( xTaskIncrementTick() != pdFALSE )\r
515         {\r
516             /* A context switch is required.  Context switching is performed in\r
517              * the PendSV interrupt.  Pend the PendSV interrupt. */\r
518             portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;\r
519         }\r
520     }\r
521 \r
522     vPortClearBASEPRIFromISR();\r
523 }\r
524 /*-----------------------------------------------------------*/\r
525 \r
526 #if ( configUSE_TICKLESS_IDLE == 1 )\r
527 \r
528     __weak void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime )\r
529     {\r
530         uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements;\r
531         TickType_t xModifiableIdleTime;\r
532 \r
533         /* Make sure the SysTick reload value does not overflow the counter. */\r
534         if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks )\r
535         {\r
536             xExpectedIdleTime = xMaximumPossibleSuppressedTicks;\r
537         }\r
538 \r
539         /* Stop the SysTick momentarily.  The time the SysTick is stopped for\r
540          * is accounted for as best it can be, but using the tickless mode will\r
541          * inevitably result in some tiny drift of the time maintained by the\r
542          * kernel with respect to calendar time. */\r
543         portNVIC_SYSTICK_CTRL_REG &= ~portNVIC_SYSTICK_ENABLE_BIT;\r
544 \r
545         /* Calculate the reload value required to wait xExpectedIdleTime\r
546          * tick periods.  -1 is used because this code will execute part way\r
547          * through one of the tick periods. */\r
548         ulReloadValue = portNVIC_SYSTICK_CURRENT_VALUE_REG + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) );\r
549 \r
550         if( ulReloadValue > ulStoppedTimerCompensation )\r
551         {\r
552             ulReloadValue -= ulStoppedTimerCompensation;\r
553         }\r
554 \r
555         /* Enter a critical section but don't use the taskENTER_CRITICAL()\r
556          * method as that will mask interrupts that should exit sleep mode. */\r
557         __disable_irq();\r
558         __dsb( portSY_FULL_READ_WRITE );\r
559         __isb( portSY_FULL_READ_WRITE );\r
560 \r
561         /* If a context switch is pending or a task is waiting for the scheduler\r
562          * to be unsuspended then abandon the low power entry. */\r
563         if( eTaskConfirmSleepModeStatus() == eAbortSleep )\r
564         {\r
565             /* Restart from whatever is left in the count register to complete\r
566              * this tick period. */\r
567             portNVIC_SYSTICK_LOAD_REG = portNVIC_SYSTICK_CURRENT_VALUE_REG;\r
568 \r
569             /* Restart SysTick. */\r
570             portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;\r
571 \r
572             /* Reset the reload register to the value required for normal tick\r
573              * periods. */\r
574             portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;\r
575 \r
576             /* Re-enable interrupts - see comments above __disable_irq() call\r
577              * above. */\r
578             __enable_irq();\r
579         }\r
580         else\r
581         {\r
582             /* Set the new reload value. */\r
583             portNVIC_SYSTICK_LOAD_REG = ulReloadValue;\r
584 \r
585             /* Clear the SysTick count flag and set the count value back to\r
586              * zero. */\r
587             portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;\r
588 \r
589             /* Restart SysTick. */\r
590             portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;\r
591 \r
592             /* Sleep until something happens.  configPRE_SLEEP_PROCESSING() can\r
593              * set its parameter to 0 to indicate that its implementation contains\r
594              * its own wait for interrupt or wait for event instruction, and so wfi\r
595              * should not be executed again.  However, the original expected idle\r
596              * time variable must remain unmodified, so a copy is taken. */\r
597             xModifiableIdleTime = xExpectedIdleTime;\r
598             configPRE_SLEEP_PROCESSING( xModifiableIdleTime );\r
599 \r
600             if( xModifiableIdleTime > 0 )\r
601             {\r
602                 __dsb( portSY_FULL_READ_WRITE );\r
603                 __wfi();\r
604                 __isb( portSY_FULL_READ_WRITE );\r
605             }\r
606 \r
607             configPOST_SLEEP_PROCESSING( xExpectedIdleTime );\r
608 \r
609             /* Re-enable interrupts to allow the interrupt that brought the MCU\r
610              * out of sleep mode to execute immediately.  see comments above\r
611              * __disable_interrupt() call above. */\r
612             __enable_irq();\r
613             __dsb( portSY_FULL_READ_WRITE );\r
614             __isb( portSY_FULL_READ_WRITE );\r
615 \r
616             /* Disable interrupts again because the clock is about to be stopped\r
617              * and interrupts that execute while the clock is stopped will increase\r
618              * any slippage between the time maintained by the RTOS and calendar\r
619              * time. */\r
620             __disable_irq();\r
621             __dsb( portSY_FULL_READ_WRITE );\r
622             __isb( portSY_FULL_READ_WRITE );\r
623 \r
624             /* Disable the SysTick clock without reading the\r
625              * portNVIC_SYSTICK_CTRL_REG register to ensure the\r
626              * portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set.  Again,\r
627              * the time the SysTick is stopped for is accounted for as best it can\r
628              * be, but using the tickless mode will inevitably result in some tiny\r
629              * drift of the time maintained by the kernel with respect to calendar\r
630              * time*/\r
631             portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT );\r
632 \r
633             /* Determine if the SysTick clock has already counted to zero and\r
634              * been set back to the current reload value (the reload back being\r
635              * correct for the entire expected idle time) or if the SysTick is yet\r
636              * to count to zero (in which case an interrupt other than the SysTick\r
637              * must have brought the system out of sleep mode). */\r
638             if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 )\r
639             {\r
640                 uint32_t ulCalculatedLoadValue;\r
641 \r
642                 /* The tick interrupt is already pending, and the SysTick count\r
643                  * reloaded with ulReloadValue.  Reset the\r
644                  * portNVIC_SYSTICK_LOAD_REG with whatever remains of this tick\r
645                  * period. */\r
646                 ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG );\r
647 \r
648                 /* Don't allow a tiny value, or values that have somehow\r
649                  * underflowed because the post sleep hook did something\r
650                  * that took too long. */\r
651                 if( ( ulCalculatedLoadValue < ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) )\r
652                 {\r
653                     ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL );\r
654                 }\r
655 \r
656                 portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue;\r
657 \r
658                 /* As the pending tick will be processed as soon as this\r
659                  * function exits, the tick value maintained by the tick is stepped\r
660                  * forward by one less than the time spent waiting. */\r
661                 ulCompleteTickPeriods = xExpectedIdleTime - 1UL;\r
662             }\r
663             else\r
664             {\r
665                 /* Something other than the tick interrupt ended the sleep.\r
666                  * Work out how long the sleep lasted rounded to complete tick\r
667                  * periods (not the ulReload value which accounted for part\r
668                  * ticks). */\r
669                 ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - portNVIC_SYSTICK_CURRENT_VALUE_REG;\r
670 \r
671                 /* How many complete tick periods passed while the processor\r
672                  * was waiting? */\r
673                 ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick;\r
674 \r
675                 /* The reload value is set to whatever fraction of a single tick\r
676                  * period remains. */\r
677                 portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements;\r
678             }\r
679 \r
680             /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG\r
681              * again, then set portNVIC_SYSTICK_LOAD_REG back to its standard\r
682              * value. */\r
683             portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;\r
684             portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT;\r
685             vTaskStepTick( ulCompleteTickPeriods );\r
686             portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL;\r
687 \r
688             /* Exit with interrupts enabled. */\r
689             __enable_irq();\r
690         }\r
691     }\r
692 \r
693 #endif /* #if configUSE_TICKLESS_IDLE */\r
694 \r
695 /*-----------------------------------------------------------*/\r
696 \r
697 /*\r
698  * Setup the SysTick timer to generate the tick interrupts at the required\r
699  * frequency.\r
700  */\r
701 #if ( configOVERRIDE_DEFAULT_TICK_CONFIGURATION == 0 )\r
702 \r
703     __weak void vPortSetupTimerInterrupt( void )\r
704     {\r
705         /* Calculate the constants required to configure the tick interrupt. */\r
706         #if ( configUSE_TICKLESS_IDLE == 1 )\r
707             {\r
708                 ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );\r
709                 xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;\r
710                 ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );\r
711             }\r
712         #endif /* configUSE_TICKLESS_IDLE */\r
713 \r
714         /* Stop and clear the SysTick. */\r
715         portNVIC_SYSTICK_CTRL_REG = 0UL;\r
716         portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;\r
717 \r
718         /* Configure SysTick to interrupt at the requested rate. */\r
719         portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;\r
720         portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );\r
721     }\r
722 \r
723 #endif /* configOVERRIDE_DEFAULT_TICK_CONFIGURATION */\r
724 /*-----------------------------------------------------------*/\r
725 \r
726 __asm uint32_t vPortGetIPSR( void )\r
727 {\r
728 /* *INDENT-OFF* */\r
729     PRESERVE8\r
730 \r
731     mrs r0, ipsr\r
732     bx r14\r
733 /* *INDENT-ON* */\r
734 }\r
735 /*-----------------------------------------------------------*/\r
736 \r
737 #if ( configASSERT_DEFINED == 1 )\r
738 \r
739     void vPortValidateInterruptPriority( void )\r
740     {\r
741         uint32_t ulCurrentInterrupt;\r
742         uint8_t ucCurrentPriority;\r
743 \r
744         /* Obtain the number of the currently executing interrupt. */\r
745         ulCurrentInterrupt = vPortGetIPSR();\r
746 \r
747         /* Is the interrupt number a user defined interrupt? */\r
748         if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER )\r
749         {\r
750             /* Look up the interrupt's priority. */\r
751             ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ];\r
752 \r
753             /* The following assertion will fail if a service routine (ISR) for\r
754              * an interrupt that has been assigned a priority above\r
755              * configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API\r
756              * function.  ISR safe FreeRTOS API functions must *only* be called\r
757              * from interrupts that have been assigned a priority at or below\r
758              * configMAX_SYSCALL_INTERRUPT_PRIORITY.\r
759              *\r
760              * Numerically low interrupt priority numbers represent logically high\r
761              * interrupt priorities, therefore the priority of the interrupt must\r
762              * be set to a value equal to or numerically *higher* than\r
763              * configMAX_SYSCALL_INTERRUPT_PRIORITY.\r
764              *\r
765              * Interrupts that  use the FreeRTOS API must not be left at their\r
766              * default priority of      zero as that is the highest possible priority,\r
767              * which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY,\r
768              * and      therefore also guaranteed to be invalid.\r
769              *\r
770              * FreeRTOS maintains separate thread and ISR API functions to ensure\r
771              * interrupt entry is as fast and simple as possible.\r
772              *\r
773              * The following links provide detailed information:\r
774              * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html\r
775              * https://www.FreeRTOS.org/FAQHelp.html */\r
776             configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );\r
777         }\r
778 \r
779         /* Priority grouping:  The interrupt controller (NVIC) allows the bits\r
780          * that define each interrupt's priority to be split between bits that\r
781          * define the interrupt's pre-emption priority bits and bits that define\r
782          * the interrupt's sub-priority.  For simplicity all bits must be defined\r
783          * to be pre-emption priority bits.  The following assertion will fail if\r
784          * this is not the case (if some bits represent a sub-priority).\r
785          *\r
786          * If the application only uses CMSIS libraries for interrupt\r
787          * configuration then the correct setting can be achieved on all Cortex-M\r
788          * devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the\r
789          * scheduler.  Note however that some vendor specific peripheral libraries\r
790          * assume a non-zero priority group setting, in which cases using a value\r
791          * of zero will result in unpredictable behaviour. */\r
792         configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue );\r
793     }\r
794 \r
795 #endif /* configASSERT_DEFINED */\r