xref: /Kernel-v10.6.2/portable/IAR/AVR_Mega0/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 
127     /* Next place the address of the hardware stack.  This is required so
128      * the AVR stack pointer can be restored to point to the hardware stack. */
129     pxTopOfHardwareStack -= portBYTES_USED_BY_RETURN_ADDRESS;
130     usAddress = ( uint16_t ) pxTopOfHardwareStack;
131 
132     /* SPL */
133     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
134     pxTopOfStack--;
135 
136     /* SPH */
137     usAddress >>= 8;
138     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
139     pxTopOfStack--;
140 
141     /* Now the remaining registers. */
142     *pxTopOfStack = ( StackType_t ) 0x01; /* R1 */
143     pxTopOfStack--;
144     *pxTopOfStack = ( StackType_t ) 0x02; /* R2 */
145     pxTopOfStack--;
146     *pxTopOfStack = ( StackType_t ) 0x03; /* R3 */
147     pxTopOfStack--;
148     *pxTopOfStack = ( StackType_t ) 0x04; /* R4 */
149     pxTopOfStack--;
150     *pxTopOfStack = ( StackType_t ) 0x05; /* R5 */
151     pxTopOfStack--;
152     *pxTopOfStack = ( StackType_t ) 0x06; /* R6 */
153     pxTopOfStack--;
154     *pxTopOfStack = ( StackType_t ) 0x07; /* R7 */
155     pxTopOfStack--;
156     *pxTopOfStack = ( StackType_t ) 0x08; /* R8 */
157     pxTopOfStack--;
158     *pxTopOfStack = ( StackType_t ) 0x09; /* R9 */
159     pxTopOfStack--;
160     *pxTopOfStack = ( StackType_t ) 0x10; /* R10 */
161     pxTopOfStack--;
162     *pxTopOfStack = ( StackType_t ) 0x11; /* R11 */
163     pxTopOfStack--;
164     *pxTopOfStack = ( StackType_t ) 0x12; /* R12 */
165     pxTopOfStack--;
166     *pxTopOfStack = ( StackType_t ) 0x13; /* R13 */
167     pxTopOfStack--;
168     *pxTopOfStack = ( StackType_t ) 0x14; /* R14 */
169     pxTopOfStack--;
170     *pxTopOfStack = ( StackType_t ) 0x15; /* R15 */
171     pxTopOfStack--;
172 
173     /* Place the parameter on the stack in the expected location. */
174     usAddress = ( uint16_t ) pvParameters;
175     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
176     pxTopOfStack--;
177 
178     usAddress >>= 8;
179     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
180     pxTopOfStack--;
181 
182     *pxTopOfStack = ( StackType_t ) 0x18; /* R18 */
183     pxTopOfStack--;
184     *pxTopOfStack = ( StackType_t ) 0x19; /* R19 */
185     pxTopOfStack--;
186     *pxTopOfStack = ( StackType_t ) 0x20; /* R20 */
187     pxTopOfStack--;
188     *pxTopOfStack = ( StackType_t ) 0x21; /* R21 */
189     pxTopOfStack--;
190     *pxTopOfStack = ( StackType_t ) 0x22; /* R22 */
191     pxTopOfStack--;
192     *pxTopOfStack = ( StackType_t ) 0x23; /* R23 */
193     pxTopOfStack--;
194     *pxTopOfStack = ( StackType_t ) 0x24; /* R24 */
195     pxTopOfStack--;
196     *pxTopOfStack = ( StackType_t ) 0x25; /* R25 */
197     pxTopOfStack--;
198     *pxTopOfStack = ( StackType_t ) 0x26; /* R26 X */
199     pxTopOfStack--;
200     *pxTopOfStack = ( StackType_t ) 0x27; /* R27 */
201     pxTopOfStack--;
202 
203     /* The Y register is not stored as it is used as the software stack and
204      * gets saved into the task control block. */
205 
206     *pxTopOfStack = ( StackType_t ) 0x30;  /* R30 Z */
207     pxTopOfStack--;
208     *pxTopOfStack = ( StackType_t ) 0x031; /* R31 */
209 
210     pxTopOfStack--;
211     *pxTopOfStack = portNO_CRITICAL_NESTING; /* Critical nesting is zero when the task starts. */
212 
213     /*lint +e950 +e611 +e923 */
214 
215     return pxTopOfStack;
216 }
217 /*-----------------------------------------------------------*/
218 
xPortStartScheduler(void)219 BaseType_t xPortStartScheduler( void )
220 {
221     /* Setup the hardware to generate the tick. */
222     prvSetupTimerInterrupt();
223 
224     /* Restore the context of the first task that is going to run.
225      * Normally we would just call portRESTORE_CONTEXT() here, but as the IAR
226      * compiler does not fully support inline assembler we have to make a call.*/
227     vPortStart();
228 
229     /* Should not get here. */
230     return pdTRUE;
231 }
232 /*-----------------------------------------------------------*/
233 
vPortEndScheduler(void)234 void vPortEndScheduler( void )
235 {
236     /* vPortEndScheduler is not implemented in this port. */
237 }
238 
239 /*-----------------------------------------------------------*/
240 
241 /*
242  * Setup timer to generate a tick interrupt.
243  */
prvSetupTimerInterrupt(void)244 static void prvSetupTimerInterrupt( void )
245 {
246     TICK_init();
247 }
248 
249 /*-----------------------------------------------------------*/
250 
251 #if configUSE_PREEMPTION == 1
252 
253 /*
254  * Tick ISR for preemptive scheduler.  We can use a naked attribute as
255  * the context is saved at the start of vPortYieldFromTick().  The tick
256  * count is incremented after the context is saved.
257  */
258 
TICK_INT(void)259     __task void TICK_INT( void )
260     {
261         vPortYieldFromTick();
262         asm ( "reti" );
263     }
264 #else
265 
266 /*
267  * Tick ISR for the cooperative scheduler.  All this does is increment the
268  * tick count.  We don't need to switch context, this can only be done by
269  * manual calls to taskYIELD();
270  */
271 
TICK_INT(void)272     __interrupt void TICK_INT( void )
273     {
274         /* Clear tick interrupt flag. */
275         INT_FLAGS = INT_MASK;
276 
277         xTaskIncrementTick();
278     }
279 #endif /* if configUSE_PREEMPTION == 1 */
280 
281 /*-----------------------------------------------------------*/
282 
vPortEnterCritical(void)283 void vPortEnterCritical( void )
284 {
285     portDISABLE_INTERRUPTS();
286     uxCriticalNesting++;
287 }
288 
289 /*-----------------------------------------------------------*/
290 
vPortExitCritical(void)291 void vPortExitCritical( void )
292 {
293     uxCriticalNesting--;
294 
295     if( uxCriticalNesting == portNO_CRITICAL_NESTING )
296     {
297         portENABLE_INTERRUPTS();
298     }
299 }
300