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 /* Scheduler includes. */
37 #include "FreeRTOS.h"
38 #include "task.h"
39 
40 /* Constants required to handle interrupts. */
41 #define portTIMER_MATCH_ISR_BIT    ( ( uint8_t ) 0x01 )
42 #define portCLEAR_VIC_INTERRUPT    ( ( uint32_t ) 0 )
43 
44 /* Constants required to handle critical sections. */
45 #define portNO_CRITICAL_NESTING    ( ( uint32_t ) 0 )
46 volatile uint32_t ulCriticalNesting = 9999UL;
47 
48 /*-----------------------------------------------------------*/
49 
50 /* ISR to handle manual context switches (from a call to taskYIELD()). */
51 void vPortYieldProcessor( void ) __attribute__( ( interrupt( "SWI" ), naked ) );
52 
53 /*
54  * The scheduler can only be started from ARM mode, hence the inclusion of this
55  * function here.
56  */
57 void vPortISRStartFirstTask( void );
58 /*-----------------------------------------------------------*/
59 
vPortISRStartFirstTask(void)60 void vPortISRStartFirstTask( void )
61 {
62     /* Simply start the scheduler.  This is included here as it can only be
63      * called from ARM mode. */
64     portRESTORE_CONTEXT();
65 }
66 /*-----------------------------------------------------------*/
67 
68 /*
69  * Called by portYIELD() or taskYIELD() to manually force a context switch.
70  *
71  * When a context switch is performed from the task level the saved task
72  * context is made to look as if it occurred from within the tick ISR.  This
73  * way the same restore context function can be used when restoring the context
74  * saved from the ISR or that saved from a call to vPortYieldProcessor.
75  */
vPortYieldProcessor(void)76 void vPortYieldProcessor( void )
77 {
78     /* Within an IRQ ISR the link register has an offset from the true return
79      * address, but an SWI ISR does not.  Add the offset manually so the same
80      * ISR return code can be used in both cases. */
81     __asm volatile ( "ADD       LR, LR, #4" );
82 
83     /* Perform the context switch.  First save the context of the current task. */
84     portSAVE_CONTEXT();
85 
86     /* Find the highest priority task that is ready to run. */
87     __asm volatile ( "bl         vTaskSwitchContext" );
88 
89     /* Restore the context of the new task. */
90     portRESTORE_CONTEXT();
91 }
92 /*-----------------------------------------------------------*/
93 
94 /*
95  * The ISR used for the scheduler tick depends on whether the cooperative or
96  * the preemptive scheduler is being used.
97  */
98 
99 
100 #if configUSE_PREEMPTION == 0
101 
102 /* The cooperative scheduler requires a normal IRQ service routine to
103  * simply increment the system tick. */
104     void vNonPreemptiveTick( void ) __attribute__( ( interrupt( "IRQ" ) ) );
vNonPreemptiveTick(void)105     void vNonPreemptiveTick( void )
106     {
107         xTaskIncrementTick();
108         T0IR = 2;
109         VICVectAddr = portCLEAR_VIC_INTERRUPT;
110     }
111 
112 #else /* if configUSE_PREEMPTION == 0 */
113 
114 /* The preemptive scheduler is defined as "naked" as the full context is
115  * saved on entry as part of the context switch. */
116     void vPreemptiveTick( void ) __attribute__( ( naked ) );
vPreemptiveTick(void)117     void vPreemptiveTick( void )
118     {
119         /* Save the context of the interrupted task. */
120         portSAVE_CONTEXT();
121 
122         /* Increment the RTOS tick count, then look for the highest priority
123          * task that is ready to run. */
124         __asm volatile
125         (
126             "   bl xTaskIncrementTick   \t\n" \
127             "   cmp r0, #0              \t\n" \
128             "   beq SkipContextSwitch   \t\n" \
129             "   bl vTaskSwitchContext   \t\n" \
130             "SkipContextSwitch:         \t\n"
131         );
132 
133         /* Ready for the next interrupt. */
134         T0IR = 2;
135         VICVectAddr = portCLEAR_VIC_INTERRUPT;
136 
137         /* Restore the context of the new task. */
138         portRESTORE_CONTEXT();
139     }
140 
141 #endif /* if configUSE_PREEMPTION == 0 */
142 /*-----------------------------------------------------------*/
143 
144 /*
145  * The interrupt management utilities can only be called from ARM mode.  When
146  * THUMB_INTERWORK is defined the utilities are defined as functions here to
147  * ensure a switch to ARM mode.  When THUMB_INTERWORK is not defined then
148  * the utilities are defined as macros in portmacro.h - as per other ports.
149  */
150 #ifdef THUMB_INTERWORK
151 
152     void vPortDisableInterruptsFromThumb( void ) __attribute__( ( naked ) );
153     void vPortEnableInterruptsFromThumb( void ) __attribute__( ( naked ) );
154 
vPortDisableInterruptsFromThumb(void)155     void vPortDisableInterruptsFromThumb( void )
156     {
157         __asm volatile (
158             "STMDB  SP!, {R0}       \n\t" /* Push R0.                                 */
159             "MRS    R0, CPSR        \n\t" /* Get CPSR.                                */
160             "ORR    R0, R0, #0xC0   \n\t" /* Disable IRQ, FIQ.                        */
161             "MSR    CPSR, R0        \n\t" /* Write back modified value.               */
162             "LDMIA  SP!, {R0}       \n\t" /* Pop R0.                                  */
163             "BX     R14" );               /* Return back to thumb.                    */
164     }
165 
vPortEnableInterruptsFromThumb(void)166     void vPortEnableInterruptsFromThumb( void )
167     {
168         __asm volatile (
169             "STMDB  SP!, {R0}       \n\t" /* Push R0.                                 */
170             "MRS    R0, CPSR        \n\t" /* Get CPSR.                                */
171             "BIC    R0, R0, #0xC0   \n\t" /* Enable IRQ, FIQ.                         */
172             "MSR    CPSR, R0        \n\t" /* Write back modified value.               */
173             "LDMIA  SP!, {R0}       \n\t" /* Pop R0.                                  */
174             "BX     R14" );               /* Return back to thumb.                    */
175     }
176 
177 #endif /* THUMB_INTERWORK */
178 
179 /* The code generated by the GCC compiler uses the stack in different ways at
180  * different optimisation levels.  The interrupt flags can therefore not always
181  * be saved to the stack.  Instead the critical section nesting level is stored
182  * in a variable, which is then saved as part of the stack context. */
vPortEnterCritical(void)183 void vPortEnterCritical( void )
184 {
185     /* Disable interrupts as per portDISABLE_INTERRUPTS();                          */
186     __asm volatile (
187         "STMDB  SP!, {R0}           \n\t" /* Push R0.                             */
188         "MRS    R0, CPSR            \n\t" /* Get CPSR.                            */
189         "ORR    R0, R0, #0xC0       \n\t" /* Disable IRQ, FIQ.                    */
190         "MSR    CPSR, R0            \n\t" /* Write back modified value.           */
191         "LDMIA  SP!, {R0}" );             /* Pop R0.                              */
192 
193     /* Now that interrupts are disabled, ulCriticalNesting can be accessed
194      * directly.  Increment ulCriticalNesting to keep a count of how many times
195      * portENTER_CRITICAL() has been called. */
196     ulCriticalNesting++;
197 }
198 
vPortExitCritical(void)199 void vPortExitCritical( void )
200 {
201     if( ulCriticalNesting > portNO_CRITICAL_NESTING )
202     {
203         /* Decrement the nesting count as we are leaving a critical section. */
204         ulCriticalNesting--;
205 
206         /* If the nesting level has reached zero then interrupts should be
207          * re-enabled. */
208         if( ulCriticalNesting == portNO_CRITICAL_NESTING )
209         {
210             /* Enable interrupts as per portEXIT_CRITICAL().                    */
211             __asm volatile (
212                 "STMDB  SP!, {R0}       \n\t" /* Push R0.                     */
213                 "MRS    R0, CPSR        \n\t" /* Get CPSR.                    */
214                 "BIC    R0, R0, #0xC0   \n\t" /* Enable IRQ, FIQ.             */
215                 "MSR    CPSR, R0        \n\t" /* Write back modified value.   */
216                 "LDMIA  SP!, {R0}" );         /* Pop R0.                      */
217         }
218     }
219 }
220