xref: /Kernel-v10.6.2/portable/IAR/AVR_AVRDx/port.c (revision ef7b253b56c9788077f5ecd6c9deb4021923d646)
1 /*
2  * FreeRTOS Kernel V10.6.2
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 #include <stdlib.h>
30 #include "porthardware.h"
31 #include "FreeRTOS.h"
32 #include "task.h"
33 
34 /*-----------------------------------------------------------
35 * Implementation of functions defined in portable.h for the AVR port.
36 *----------------------------------------------------------*/
37 
38 /* Start tasks with interrupts enables. */
39 #define portFLAGS_INT_ENABLED    ( ( StackType_t ) 0x80 )
40 
41 /*-----------------------------------------------------------*/
42 
43 
44 #define portBYTES_USED_BY_RETURN_ADDRESS    2
45 #define portNO_CRITICAL_NESTING             ( ( UBaseType_t ) 0 )
46 
47 /* Stores the critical section nesting.  This must not be initialised to 0.
48  * It will be initialised when a task starts. */
49 UBaseType_t uxCriticalNesting = 0x50;
50 
51 /*
52  * Setup timer to generate a tick interrupt.
53  */
54 static void prvSetupTimerInterrupt( void );
55 
56 /*
57  * The IAR compiler does not have full support for inline assembler, so
58  * these are defined in the portmacro assembler file.
59  */
60 extern void vPortYieldFromTick( void );
61 extern void vPortStart( void );
62 
63 /*-----------------------------------------------------------*/
64 
65 /*
66  * See header file for description.
67  */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)68 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
69                                      TaskFunction_t pxCode,
70                                      void * pvParameters )
71 {
72     uint16_t usAddress;
73     StackType_t * pxTopOfHardwareStack;
74 
75     /* Simulate how the stack would look after a call to vPortYield(). */
76 
77     /*lint -e950 -e611 -e923 Lint doesn't like this much - but nothing I can do about it. */
78 
79     /* The IAR compiler requires two stacks per task.  First there is the
80      * hardware call stack which uses the AVR stack pointer.  Second there is the
81      * software stack (local variables, parameter passing, etc.) which uses the
82      * AVR Y register.
83      * This function places both stacks within the memory block passed in as the
84      * first parameter.  The hardware stack is placed at the bottom of the memory
85      * block.  A gap is then left for the hardware stack to grow.  Next the software
86      * stack is placed.  The amount of space between the software and hardware
87      * stacks is defined by configCALL_STACK_SIZE.
88      * The first part of the stack is the hardware stack.  Place the start
89      * address of the task on the hardware stack. */
90 
91     /* Place a few bytes of known values on the bottom of the stack.
92      * This is just useful for debugging. */
93     /**pxTopOfStack = 0x11; */
94     /*pxTopOfStack--; */
95     /**pxTopOfStack = 0x22; */
96     /*pxTopOfStack--; */
97     /**pxTopOfStack = 0x33; */
98     /*pxTopOfStack--; */
99 
100     /* Remember where the top of the hardware stack is - this is required
101      * below. */
102     pxTopOfHardwareStack = pxTopOfStack;
103 
104     usAddress = ( uint16_t ) pxCode;
105     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
106     pxTopOfStack--;
107 
108     usAddress >>= 8;
109     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
110     pxTopOfStack--;
111 
112     /* Leave enough space for the hardware stack before starting the software
113      * stack.  The '- 2' is because we have already used two spaces for the
114      * address of the start of the task. */
115     pxTopOfStack -= ( configCALL_STACK_SIZE - 2 );
116 
117     /* Next simulate the stack as if after a call to portSAVE_CONTEXT().
118      *  portSAVE_CONTEXT places the flags on the stack immediately after r0
119      *  to ensure the interrupts get disabled as soon as possible, and so ensuring
120      *  the stack use is minimal should a context switch interrupt occur. */
121 
122     *pxTopOfStack = ( StackType_t ) 0x00; /* R0 */
123     pxTopOfStack--;
124     *pxTopOfStack = portFLAGS_INT_ENABLED;
125     pxTopOfStack--;
126     *pxTopOfStack = ( StackType_t ) 0x00; /* RAMPZ */
127     pxTopOfStack--;
128 
129     /* Next place the address of the hardware stack.  This is required so
130      * the AVR stack pointer can be restored to point to the hardware stack. */
131     pxTopOfHardwareStack -= portBYTES_USED_BY_RETURN_ADDRESS;
132     usAddress = ( uint16_t ) pxTopOfHardwareStack;
133 
134     /* SPL */
135     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
136     pxTopOfStack--;
137 
138     /* SPH */
139     usAddress >>= 8;
140     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
141     pxTopOfStack--;
142 
143     /* Now the remaining registers. */
144     *pxTopOfStack = ( StackType_t ) 0x01; /* R1 */
145     pxTopOfStack--;
146     *pxTopOfStack = ( StackType_t ) 0x02; /* R2 */
147     pxTopOfStack--;
148     *pxTopOfStack = ( StackType_t ) 0x03; /* R3 */
149     pxTopOfStack--;
150     *pxTopOfStack = ( StackType_t ) 0x04; /* R4 */
151     pxTopOfStack--;
152     *pxTopOfStack = ( StackType_t ) 0x05; /* R5 */
153     pxTopOfStack--;
154     *pxTopOfStack = ( StackType_t ) 0x06; /* R6 */
155     pxTopOfStack--;
156     *pxTopOfStack = ( StackType_t ) 0x07; /* R7 */
157     pxTopOfStack--;
158     *pxTopOfStack = ( StackType_t ) 0x08; /* R8 */
159     pxTopOfStack--;
160     *pxTopOfStack = ( StackType_t ) 0x09; /* R9 */
161     pxTopOfStack--;
162     *pxTopOfStack = ( StackType_t ) 0x10; /* R10 */
163     pxTopOfStack--;
164     *pxTopOfStack = ( StackType_t ) 0x11; /* R11 */
165     pxTopOfStack--;
166     *pxTopOfStack = ( StackType_t ) 0x12; /* R12 */
167     pxTopOfStack--;
168     *pxTopOfStack = ( StackType_t ) 0x13; /* R13 */
169     pxTopOfStack--;
170     *pxTopOfStack = ( StackType_t ) 0x14; /* R14 */
171     pxTopOfStack--;
172     *pxTopOfStack = ( StackType_t ) 0x15; /* R15 */
173     pxTopOfStack--;
174 
175     /* Place the parameter on the stack in the expected location. */
176     usAddress = ( uint16_t ) pvParameters;
177     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
178     pxTopOfStack--;
179 
180     usAddress >>= 8;
181     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
182     pxTopOfStack--;
183 
184     *pxTopOfStack = ( StackType_t ) 0x18; /* R18 */
185     pxTopOfStack--;
186     *pxTopOfStack = ( StackType_t ) 0x19; /* R19 */
187     pxTopOfStack--;
188     *pxTopOfStack = ( StackType_t ) 0x20; /* R20 */
189     pxTopOfStack--;
190     *pxTopOfStack = ( StackType_t ) 0x21; /* R21 */
191     pxTopOfStack--;
192     *pxTopOfStack = ( StackType_t ) 0x22; /* R22 */
193     pxTopOfStack--;
194     *pxTopOfStack = ( StackType_t ) 0x23; /* R23 */
195     pxTopOfStack--;
196     *pxTopOfStack = ( StackType_t ) 0x24; /* R24 */
197     pxTopOfStack--;
198     *pxTopOfStack = ( StackType_t ) 0x25; /* R25 */
199     pxTopOfStack--;
200     *pxTopOfStack = ( StackType_t ) 0x26; /* R26 X */
201     pxTopOfStack--;
202     *pxTopOfStack = ( StackType_t ) 0x27; /* R27 */
203     pxTopOfStack--;
204 
205     /* The Y register is not stored as it is used as the software stack and
206      * gets saved into the task control block. */
207 
208     *pxTopOfStack = ( StackType_t ) 0x30;  /* R30 Z */
209     pxTopOfStack--;
210     *pxTopOfStack = ( StackType_t ) 0x031; /* R31 */
211 
212     pxTopOfStack--;
213     *pxTopOfStack = portNO_CRITICAL_NESTING; /* Critical nesting is zero when the task starts. */
214 
215     /*lint +e950 +e611 +e923 */
216 
217     return pxTopOfStack;
218 }
219 /*-----------------------------------------------------------*/
220 
xPortStartScheduler(void)221 BaseType_t xPortStartScheduler( void )
222 {
223     /* Setup the hardware to generate the tick. */
224     prvSetupTimerInterrupt();
225 
226     /* Restore the context of the first task that is going to run.
227      * Normally we would just call portRESTORE_CONTEXT() here, but as the IAR
228      * compiler does not fully support inline assembler we have to make a call.*/
229     vPortStart();
230 
231     /* Should not get here. */
232     return pdTRUE;
233 }
234 /*-----------------------------------------------------------*/
235 
vPortEndScheduler(void)236 void vPortEndScheduler( void )
237 {
238     /* vPortEndScheduler is not implemented in this port. */
239 }
240 
241 /*-----------------------------------------------------------*/
242 
243 /*
244  * Setup timer to generate a tick interrupt.
245  */
prvSetupTimerInterrupt(void)246 static void prvSetupTimerInterrupt( void )
247 {
248     TICK_init();
249 }
250 
251 /*-----------------------------------------------------------*/
252 
253 #if configUSE_PREEMPTION == 1
254 
255 /*
256  * Tick ISR for preemptive scheduler.  We can use a naked attribute as
257  * the context is saved at the start of vPortYieldFromTick().  The tick
258  * count is incremented after the context is saved.
259  */
260 
TICK_INT(void)261     __task void TICK_INT( void )
262     {
263         vPortYieldFromTick();
264         asm ( "reti" );
265     }
266 #else
267 
268 /*
269  * Tick ISR for the cooperative scheduler.  All this does is increment the
270  * tick count.  We don't need to switch context, this can only be done by
271  * manual calls to taskYIELD();
272  */
273 
TICK_INT(void)274     __interrupt void TICK_INT( void )
275     {
276         /* Clear tick interrupt flag. */
277         INT_FLAGS = INT_MASK;
278 
279         xTaskIncrementTick();
280     }
281 #endif /* if configUSE_PREEMPTION == 1 */
282 
283 /*-----------------------------------------------------------*/
284 
vPortEnterCritical(void)285 void vPortEnterCritical( void )
286 {
287     portDISABLE_INTERRUPTS();
288     uxCriticalNesting++;
289 }
290 
291 /*-----------------------------------------------------------*/
292 
vPortExitCritical(void)293 void vPortExitCritical( void )
294 {
295     uxCriticalNesting--;
296 
297     if( uxCriticalNesting == portNO_CRITICAL_NESTING )
298     {
299         portENABLE_INTERRUPTS();
300     }
301 }
302