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 /* Standard includes. */
31 #include <stdlib.h>
32 
33 /* Scheduler includes. */
34 #include "FreeRTOS.h"
35 #include "task.h"
36 
37 /* Constants required to setup the initial task context. */
38 #define portINITIAL_SPSR                   ( ( StackType_t ) 0x1f ) /* System mode, ARM mode, interrupts enabled. */
39 #define portTHUMB_MODE_BIT                 ( ( StackType_t ) 0x20 )
40 #define portINSTRUCTION_SIZE               ( ( StackType_t ) 4 )
41 #define portNO_CRITICAL_SECTION_NESTING    ( ( StackType_t ) 0 )
42 
43 /* Constants required to setup the tick ISR. */
44 #define portENABLE_TIMER                   ( ( uint8_t ) 0x01 )
45 #define portPRESCALE_VALUE                 0x00
46 #define portINTERRUPT_ON_MATCH             ( ( uint32_t ) 0x01 )
47 #define portRESET_COUNT_ON_MATCH           ( ( uint32_t ) 0x02 )
48 
49 /* Constants required to setup the VIC for the tick ISR. */
50 #define portTIMER_VIC_CHANNEL              ( ( uint32_t ) 0x0004 )
51 #define portTIMER_VIC_CHANNEL_BIT          ( ( uint32_t ) 0x0010 )
52 #define portTIMER_VIC_ENABLE               ( ( uint32_t ) 0x0020 )
53 
54 /* Constants required to handle interrupts. */
55 #define portTIMER_MATCH_ISR_BIT            ( ( uint8_t ) 0x01 )
56 #define portCLEAR_VIC_INTERRUPT            ( ( uint32_t ) 0 )
57 
58 /*-----------------------------------------------------------*/
59 
60 /* The code generated by the Keil compiler does not maintain separate
61  * stack and frame pointers. The portENTER_CRITICAL macro cannot therefore
62  * use the stack as per other ports.  Instead a variable is used to keep
63  * track of the critical section nesting.  This variable has to be stored
64  * as part of the task context and must be initialised to a non zero value. */
65 
66 #define portNO_CRITICAL_NESTING    ( ( uint32_t ) 0 )
67 volatile uint32_t ulCriticalNesting = 9999UL;
68 
69 /*-----------------------------------------------------------*/
70 
71 /* Setup the timer to generate the tick interrupts. */
72 static void prvSetupTimerInterrupt( void );
73 
74 /*
75  * The scheduler can only be started from ARM mode, so
76  * vPortStartFirstSTask() is defined in portISR.c.
77  */
78 extern __asm void vPortStartFirstTask( void );
79 
80 /*-----------------------------------------------------------*/
81 
82 /*
83  * See header file for description.
84  */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)85 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
86                                      TaskFunction_t pxCode,
87                                      void * pvParameters )
88 {
89     StackType_t * pxOriginalTOS;
90 
91     /* Setup the initial stack of the task.  The stack is set exactly as
92      * expected by the portRESTORE_CONTEXT() macro.
93      *
94      * Remember where the top of the (simulated) stack is before we place
95      * anything on it. */
96     pxOriginalTOS = pxTopOfStack;
97 
98     /* To ensure asserts in tasks.c don't fail, although in this case the assert
99      * is not really required. */
100     pxTopOfStack--;
101 
102     /* First on the stack is the return address - which in this case is the
103      * start of the task.  The offset is added to make the return address appear
104      * as it would within an IRQ ISR. */
105     *pxTopOfStack = ( StackType_t ) pxCode + portINSTRUCTION_SIZE;
106     pxTopOfStack--;
107 
108     *pxTopOfStack = ( StackType_t ) 0xaaaaaaaa;    /* R14 */
109     pxTopOfStack--;
110     *pxTopOfStack = ( StackType_t ) pxOriginalTOS; /* Stack used when task starts goes in R13. */
111     pxTopOfStack--;
112     *pxTopOfStack = ( StackType_t ) 0x12121212;    /* R12 */
113     pxTopOfStack--;
114     *pxTopOfStack = ( StackType_t ) 0x11111111;    /* R11 */
115     pxTopOfStack--;
116     *pxTopOfStack = ( StackType_t ) 0x10101010;    /* R10 */
117     pxTopOfStack--;
118     *pxTopOfStack = ( StackType_t ) 0x09090909;    /* R9 */
119     pxTopOfStack--;
120     *pxTopOfStack = ( StackType_t ) 0x08080808;    /* R8 */
121     pxTopOfStack--;
122     *pxTopOfStack = ( StackType_t ) 0x07070707;    /* R7 */
123     pxTopOfStack--;
124     *pxTopOfStack = ( StackType_t ) 0x06060606;    /* R6 */
125     pxTopOfStack--;
126     *pxTopOfStack = ( StackType_t ) 0x05050505;    /* R5 */
127     pxTopOfStack--;
128     *pxTopOfStack = ( StackType_t ) 0x04040404;    /* R4 */
129     pxTopOfStack--;
130     *pxTopOfStack = ( StackType_t ) 0x03030303;    /* R3 */
131     pxTopOfStack--;
132     *pxTopOfStack = ( StackType_t ) 0x02020202;    /* R2 */
133     pxTopOfStack--;
134     *pxTopOfStack = ( StackType_t ) 0x01010101;    /* R1 */
135     pxTopOfStack--;
136     *pxTopOfStack = ( StackType_t ) pvParameters;  /* R0 */
137     pxTopOfStack--;
138 
139     /* The last thing onto the stack is the status register, which is set for
140      * system mode, with interrupts enabled. */
141     *pxTopOfStack = ( StackType_t ) portINITIAL_SPSR;
142 
143     if( ( ( uint32_t ) pxCode & 0x01UL ) != 0x00UL )
144     {
145         /* We want the task to start in thumb mode. */
146         *pxTopOfStack |= portTHUMB_MODE_BIT;
147     }
148 
149     pxTopOfStack--;
150 
151     /* The code generated by the Keil compiler does not maintain separate
152      * stack and frame pointers. The portENTER_CRITICAL macro cannot therefore
153      * use the stack as per other ports.  Instead a variable is used to keep
154      * track of the critical section nesting.  This variable has to be stored
155      * as part of the task context and is initially set to zero. */
156     *pxTopOfStack = portNO_CRITICAL_SECTION_NESTING;
157 
158     return pxTopOfStack;
159 }
160 /*-----------------------------------------------------------*/
161 
xPortStartScheduler(void)162 BaseType_t xPortStartScheduler( void )
163 {
164     /* Start the timer that generates the tick ISR. */
165     prvSetupTimerInterrupt();
166 
167     /* Start the first task.  This is done from portISR.c as ARM mode must be
168      * used. */
169     vPortStartFirstTask();
170 
171     /* Should not get here! */
172     return 0;
173 }
174 /*-----------------------------------------------------------*/
175 
vPortEndScheduler(void)176 void vPortEndScheduler( void )
177 {
178     /* It is unlikely that the ARM port will require this function as there
179      * is nothing to return to.  If this is required - stop the tick ISR then
180      * return back to main. */
181 }
182 /*-----------------------------------------------------------*/
183 
184 #if configUSE_PREEMPTION == 0
185 
186 /*
187  * The cooperative scheduler requires a normal IRQ service routine to
188  * simply increment the system tick.
189  */
190     void vNonPreemptiveTick( void ) __irq;
vNonPreemptiveTick(void)191     void vNonPreemptiveTick( void ) __irq
192     {
193         /* Increment the tick count - this may make a delaying task ready
194          * to run - but a context switch is not performed. */
195         xTaskIncrementTick();
196 
197         T0IR = portTIMER_MATCH_ISR_BIT;        /* Clear the timer event */
198         VICVectAddr = portCLEAR_VIC_INTERRUPT; /* Acknowledge the Interrupt */
199     }
200 
201 #else /* if configUSE_PREEMPTION == 0 */
202 
203 /*
204  **************************************************************************
205  * The preemptive scheduler ISR is written in assembler and can be found
206  * in the portASM.s file. This will only get used if portUSE_PREEMPTION
207  * is set to 1 in portmacro.h
208  **************************************************************************
209  */
210 
211     void vPreemptiveTick( void );
212 
213 #endif /* if configUSE_PREEMPTION == 0 */
214 /*-----------------------------------------------------------*/
215 
prvSetupTimerInterrupt(void)216 static void prvSetupTimerInterrupt( void )
217 {
218     uint32_t ulCompareMatch;
219 
220     /* A 1ms tick does not require the use of the timer prescale.  This is
221      * defaulted to zero but can be used if necessary. */
222     T0PR = portPRESCALE_VALUE;
223 
224     /* Calculate the match value required for our wanted tick rate. */
225     ulCompareMatch = configCPU_CLOCK_HZ / configTICK_RATE_HZ;
226 
227     /* Protect against divide by zero.  Using an if() statement still results
228      * in a warning - hence the #if. */
229     #if portPRESCALE_VALUE != 0
230     {
231         ulCompareMatch /= ( portPRESCALE_VALUE + 1 );
232     }
233     #endif
234 
235     T0MR0 = ulCompareMatch;
236 
237     /* Generate tick with timer 0 compare match. */
238     T0MCR = portRESET_COUNT_ON_MATCH | portINTERRUPT_ON_MATCH;
239 
240     /* Setup the VIC for the timer. */
241     VICIntSelect &= ~( portTIMER_VIC_CHANNEL_BIT );
242     VICIntEnable |= portTIMER_VIC_CHANNEL_BIT;
243 
244     /* The ISR installed depends on whether the preemptive or cooperative
245      * scheduler is being used. */
246     #if configUSE_PREEMPTION == 1
247     {
248         VICVectAddr0 = ( uint32_t ) vPreemptiveTick;
249     }
250     #else
251     {
252         VICVectAddr0 = ( uint32_t ) vNonPreemptiveTick;
253     }
254     #endif
255 
256     VICVectCntl0 = portTIMER_VIC_CHANNEL | portTIMER_VIC_ENABLE;
257 
258     /* Start the timer - interrupts are disabled when this function is called
259      * so it is okay to do this here. */
260     T0TCR = portENABLE_TIMER;
261 }
262 /*-----------------------------------------------------------*/
263 
vPortEnterCritical(void)264 void vPortEnterCritical( void )
265 {
266     /* Disable interrupts as per portDISABLE_INTERRUPTS();                          */
267     __disable_irq();
268 
269     /* Now that interrupts are disabled, ulCriticalNesting can be accessed
270      * directly.  Increment ulCriticalNesting to keep a count of how many times
271      * portENTER_CRITICAL() has been called. */
272     ulCriticalNesting++;
273 }
274 /*-----------------------------------------------------------*/
275 
vPortExitCritical(void)276 void vPortExitCritical( void )
277 {
278     if( ulCriticalNesting > portNO_CRITICAL_NESTING )
279     {
280         /* Decrement the nesting count as we are leaving a critical section. */
281         ulCriticalNesting--;
282 
283         /* If the nesting level has reached zero then interrupts should be
284          * re-enabled. */
285         if( ulCriticalNesting == portNO_CRITICAL_NESTING )
286         {
287             /* Enable interrupts as per portEXIT_CRITICAL(). */
288             __enable_irq();
289         }
290     }
291 }
292 /*-----------------------------------------------------------*/
293