1 /*
2  * FreeRTOS Kernel V11.1.0
3  * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * https://www.FreeRTOS.org
25  * https://github.com/FreeRTOS
26  *
27  */
28 
29 
30 /*-----------------------------------------------------------
31 * Components that can be compiled to either ARM or THUMB mode are
32 * contained in port.c  The ISR routines, which can only be compiled
33 * to ARM mode, are contained in this file.
34 *----------------------------------------------------------*/
35 
36 /*
37  *  Changes from V2.5.2
38  *
39  + The critical section management functions have been changed.  These no
40  +    longer modify the stack and are safe to use at all optimisation levels.
41  +    The functions are now also the same for both ARM and THUMB modes.
42  +
43  +  Changes from V2.6.0
44  +
45  + Removed the 'static' from the definition of vNonPreemptiveTick() to
46  +    allow the demo to link when using the cooperative scheduler.
47  +
48  +  Changes from V3.2.4
49  +
50  + The assembler statements are now included in a single asm block rather
51  +    than each line having its own asm block.
52  */
53 
54 
55 /* Scheduler includes. */
56 #include "FreeRTOS.h"
57 
58 /* Constants required to handle interrupts. */
59 #define portTIMER_MATCH_ISR_BIT    ( ( uint8_t ) 0x01 )
60 #define portCLEAR_VIC_INTERRUPT    ( ( uint32_t ) 0 )
61 
62 /* Constants required to handle critical sections. */
63 #define portNO_CRITICAL_NESTING    ( ( uint32_t ) 0 )
64 volatile uint32_t ulCriticalNesting = 9999UL;
65 
66 /*-----------------------------------------------------------*/
67 
68 /* ISR to handle manual context switches (from a call to taskYIELD()). */
69 void vPortYieldProcessor( void ) __attribute__( ( interrupt( "SWI" ), naked ) );
70 
71 /*
72  * The scheduler can only be started from ARM mode, hence the inclusion of this
73  * function here.
74  */
75 void vPortISRStartFirstTask( void );
76 /*-----------------------------------------------------------*/
77 
vPortISRStartFirstTask(void)78 void vPortISRStartFirstTask( void )
79 {
80     /* Simply start the scheduler.  This is included here as it can only be
81      * called from ARM mode. */
82     portRESTORE_CONTEXT();
83 }
84 /*-----------------------------------------------------------*/
85 
86 /*
87  * Called by portYIELD() or taskYIELD() to manually force a context switch.
88  *
89  * When a context switch is performed from the task level the saved task
90  * context is made to look as if it occurred from within the tick ISR.  This
91  * way the same restore context function can be used when restoring the context
92  * saved from the ISR or that saved from a call to vPortYieldProcessor.
93  */
vPortYieldProcessor(void)94 void vPortYieldProcessor( void )
95 {
96     /* Within an IRQ ISR the link register has an offset from the true return
97      * address, but an SWI ISR does not.  Add the offset manually so the same
98      * ISR return code can be used in both cases. */
99     __asm volatile ( "ADD       LR, LR, #4" );
100 
101     /* Perform the context switch.  First save the context of the current task. */
102     portSAVE_CONTEXT();
103 
104     /* Find the highest priority task that is ready to run. */
105     __asm volatile ( "bl vTaskSwitchContext" );
106 
107     /* Restore the context of the new task. */
108     portRESTORE_CONTEXT();
109 }
110 /*-----------------------------------------------------------*/
111 
112 /*
113  * The ISR used for the scheduler tick.
114  */
115 void vTickISR( void ) __attribute__( ( naked ) );
vTickISR(void)116 void vTickISR( void )
117 {
118     /* Save the context of the interrupted task. */
119     portSAVE_CONTEXT();
120 
121     /* Increment the RTOS tick count, then look for the highest priority
122      * task that is ready to run. */
123     __asm volatile
124     (
125         "   bl xTaskIncrementTick   \t\n" \
126         "   cmp r0, #0              \t\n" \
127         "   beq SkipContextSwitch   \t\n" \
128         "   bl vTaskSwitchContext   \t\n" \
129         "SkipContextSwitch:         \t\n"
130     );
131 
132     /* Ready for the next interrupt. */
133     T0_IR = portTIMER_MATCH_ISR_BIT;
134     VICVectAddr = portCLEAR_VIC_INTERRUPT;
135 
136     /* Restore the context of the new task. */
137     portRESTORE_CONTEXT();
138 }
139 /*-----------------------------------------------------------*/
140 
141 /*
142  * The interrupt management utilities can only be called from ARM mode.  When
143  * THUMB_INTERWORK is defined the utilities are defined as functions here to
144  * ensure a switch to ARM mode.  When THUMB_INTERWORK is not defined then
145  * the utilities are defined as macros in portmacro.h - as per other ports.
146  */
147 #ifdef THUMB_INTERWORK
148 
149     void vPortDisableInterruptsFromThumb( void ) __attribute__( ( naked ) );
150     void vPortEnableInterruptsFromThumb( void ) __attribute__( ( naked ) );
151 
vPortDisableInterruptsFromThumb(void)152     void vPortDisableInterruptsFromThumb( void )
153     {
154         __asm volatile (
155             "STMDB  SP!, {R0}       \n\t" /* Push R0.                                 */
156             "MRS    R0, CPSR        \n\t" /* Get CPSR.                                */
157             "ORR    R0, R0, #0xC0   \n\t" /* Disable IRQ, FIQ.                        */
158             "MSR    CPSR, R0        \n\t" /* Write back modified value.               */
159             "LDMIA  SP!, {R0}       \n\t" /* Pop R0.                                  */
160             "BX     R14" );               /* Return back to thumb.                    */
161     }
162 
vPortEnableInterruptsFromThumb(void)163     void vPortEnableInterruptsFromThumb( void )
164     {
165         __asm volatile (
166             "STMDB  SP!, {R0}       \n\t" /* Push R0.                                 */
167             "MRS    R0, CPSR        \n\t" /* Get CPSR.                                */
168             "BIC    R0, R0, #0xC0   \n\t" /* Enable IRQ, FIQ.                         */
169             "MSR    CPSR, R0        \n\t" /* Write back modified value.               */
170             "LDMIA  SP!, {R0}       \n\t" /* Pop R0.                                  */
171             "BX     R14" );               /* Return back to thumb.                    */
172     }
173 
174 #endif /* THUMB_INTERWORK */
175 
176 /* The code generated by the GCC compiler uses the stack in different ways at
177  * different optimisation levels.  The interrupt flags can therefore not always
178  * be saved to the stack.  Instead the critical section nesting level is stored
179  * in a variable, which is then saved as part of the stack context. */
vPortEnterCritical(void)180 void vPortEnterCritical( void )
181 {
182     /* Disable interrupts as per portDISABLE_INTERRUPTS();                          */
183     __asm volatile (
184         "STMDB  SP!, {R0}           \n\t" /* Push R0.                             */
185         "MRS    R0, CPSR            \n\t" /* Get CPSR.                            */
186         "ORR    R0, R0, #0xC0       \n\t" /* Disable IRQ, FIQ.                    */
187         "MSR    CPSR, R0            \n\t" /* Write back modified value.           */
188         "LDMIA  SP!, {R0}" );             /* Pop R0.                              */
189 
190     /* Now that interrupts are disabled, ulCriticalNesting can be accessed
191      * directly.  Increment ulCriticalNesting to keep a count of how many times
192      * portENTER_CRITICAL() has been called. */
193     ulCriticalNesting++;
194 }
195 
vPortExitCritical(void)196 void vPortExitCritical( void )
197 {
198     if( ulCriticalNesting > portNO_CRITICAL_NESTING )
199     {
200         /* Decrement the nesting count as we are leaving a critical section. */
201         ulCriticalNesting--;
202 
203         /* If the nesting level has reached zero then interrupts should be
204          * re-enabled. */
205         if( ulCriticalNesting == portNO_CRITICAL_NESTING )
206         {
207             /* Enable interrupts as per portEXIT_CRITICAL().                    */
208             __asm volatile (
209                 "STMDB  SP!, {R0}       \n\t" /* Push R0.                     */
210                 "MRS    R0, CPSR        \n\t" /* Get CPSR.                    */
211                 "BIC    R0, R0, #0xC0   \n\t" /* Enable IRQ, FIQ.             */
212                 "MSR    CPSR, R0        \n\t" /* Write back modified value.   */
213                 "LDMIA  SP!, {R0}" );         /* Pop R0.                      */
214         }
215     }
216 }
217