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