]> begriffs open source - freertos/blob - portable/IAR/AVR_AVRDx/port.c
Add SMP in the License Header (#402)
[freertos] / portable / IAR / AVR_AVRDx / port.c
1 /*
2  * FreeRTOS SMP Kernel V202110.00
3  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9  * the Software, and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software. If you wish to use our Amazon
14  * FreeRTOS name, please do so in a fair use way that does not cause confusion.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  *
23  * https://www.FreeRTOS.org
24  * https://github.com/FreeRTOS
25  *
26  */
27
28 #include <stdlib.h>
29 #include "porthardware.h"
30 #include "FreeRTOS.h"
31 #include "task.h"
32
33 /*-----------------------------------------------------------
34 * Implementation of functions defined in portable.h for the AVR port.
35 *----------------------------------------------------------*/
36
37 /* Start tasks with interrupts enables. */
38 #define portFLAGS_INT_ENABLED    ( ( StackType_t ) 0x80 )
39
40 /*-----------------------------------------------------------*/
41
42
43 #define portBYTES_USED_BY_RETURN_ADDRESS    2
44 #define portNO_CRITICAL_NESTING             ( ( UBaseType_t ) 0 )
45
46 /* Stores the critical section nesting.  This must not be initialised to 0.
47  * It will be initialised when a task starts. */
48 UBaseType_t uxCriticalNesting = 0x50;
49
50 /*
51  * Setup timer to generate a tick interrupt.
52  */
53 static void prvSetupTimerInterrupt( void );
54
55 /*
56  * The IAR compiler does not have full support for inline assembler, so
57  * these are defined in the portmacro assembler file.
58  */
59 extern void vPortYieldFromTick( void );
60 extern void vPortStart( void );
61
62 /*-----------------------------------------------------------*/
63
64 /*
65  * See header file for description.
66  */
67 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
68                                      TaskFunction_t pxCode,
69                                      void * pvParameters )
70 {
71     uint16_t usAddress;
72     StackType_t * pxTopOfHardwareStack;
73
74     /* Simulate how the stack would look after a call to vPortYield(). */
75
76     /*lint -e950 -e611 -e923 Lint doesn't like this much - but nothing I can do about it. */
77
78     /* The IAR compiler requires two stacks per task.  First there is the
79      * hardware call stack which uses the AVR stack pointer.  Second there is the
80      * software stack (local variables, parameter passing, etc.) which uses the
81      * AVR Y register.
82      * This function places both stacks within the memory block passed in as the
83      * first parameter.  The hardware stack is placed at the bottom of the memory
84      * block.  A gap is then left for the hardware stack to grow.  Next the software
85      * stack is placed.  The amount of space between the software and hardware
86      * stacks is defined by configCALL_STACK_SIZE.
87      * The first part of the stack is the hardware stack.  Place the start
88      * address of the task on the hardware stack. */
89
90     /* Place a few bytes of known values on the bottom of the stack.
91      * This is just useful for debugging. */
92     /**pxTopOfStack = 0x11; */
93     /*pxTopOfStack--; */
94     /**pxTopOfStack = 0x22; */
95     /*pxTopOfStack--; */
96     /**pxTopOfStack = 0x33; */
97     /*pxTopOfStack--; */
98
99     /* Remember where the top of the hardware stack is - this is required
100      * below. */
101     pxTopOfHardwareStack = pxTopOfStack;
102
103     usAddress = ( uint16_t ) pxCode;
104     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
105     pxTopOfStack--;
106
107     usAddress >>= 8;
108     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
109     pxTopOfStack--;
110
111     /* Leave enough space for the hardware stack before starting the software
112      * stack.  The '- 2' is because we have already used two spaces for the
113      * address of the start of the task. */
114     pxTopOfStack -= ( configCALL_STACK_SIZE - 2 );
115
116     /* Next simulate the stack as if after a call to portSAVE_CONTEXT().
117      *  portSAVE_CONTEXT places the flags on the stack immediately after r0
118      *  to ensure the interrupts get disabled as soon as possible, and so ensuring
119      *  the stack use is minimal should a context switch interrupt occur. */
120
121     *pxTopOfStack = ( StackType_t ) 0x00; /* R0 */
122     pxTopOfStack--;
123     *pxTopOfStack = portFLAGS_INT_ENABLED;
124     pxTopOfStack--;
125     *pxTopOfStack = ( StackType_t ) 0x00; /* RAMPZ */
126     pxTopOfStack--;
127
128     /* Next place the address of the hardware stack.  This is required so
129      * the AVR stack pointer can be restored to point to the hardware stack. */
130     pxTopOfHardwareStack -= portBYTES_USED_BY_RETURN_ADDRESS;
131     usAddress = ( uint16_t ) pxTopOfHardwareStack;
132
133     /* SPL */
134     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
135     pxTopOfStack--;
136
137     /* SPH */
138     usAddress >>= 8;
139     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
140     pxTopOfStack--;
141
142     /* Now the remaining registers. */
143     *pxTopOfStack = ( StackType_t ) 0x01; /* R1 */
144     pxTopOfStack--;
145     *pxTopOfStack = ( StackType_t ) 0x02; /* R2 */
146     pxTopOfStack--;
147     *pxTopOfStack = ( StackType_t ) 0x03; /* R3 */
148     pxTopOfStack--;
149     *pxTopOfStack = ( StackType_t ) 0x04; /* R4 */
150     pxTopOfStack--;
151     *pxTopOfStack = ( StackType_t ) 0x05; /* R5 */
152     pxTopOfStack--;
153     *pxTopOfStack = ( StackType_t ) 0x06; /* R6 */
154     pxTopOfStack--;
155     *pxTopOfStack = ( StackType_t ) 0x07; /* R7 */
156     pxTopOfStack--;
157     *pxTopOfStack = ( StackType_t ) 0x08; /* R8 */
158     pxTopOfStack--;
159     *pxTopOfStack = ( StackType_t ) 0x09; /* R9 */
160     pxTopOfStack--;
161     *pxTopOfStack = ( StackType_t ) 0x10; /* R10 */
162     pxTopOfStack--;
163     *pxTopOfStack = ( StackType_t ) 0x11; /* R11 */
164     pxTopOfStack--;
165     *pxTopOfStack = ( StackType_t ) 0x12; /* R12 */
166     pxTopOfStack--;
167     *pxTopOfStack = ( StackType_t ) 0x13; /* R13 */
168     pxTopOfStack--;
169     *pxTopOfStack = ( StackType_t ) 0x14; /* R14 */
170     pxTopOfStack--;
171     *pxTopOfStack = ( StackType_t ) 0x15; /* R15 */
172     pxTopOfStack--;
173
174     /* Place the parameter on the stack in the expected location. */
175     usAddress = ( uint16_t ) pvParameters;
176     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
177     pxTopOfStack--;
178
179     usAddress >>= 8;
180     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
181     pxTopOfStack--;
182
183     *pxTopOfStack = ( StackType_t ) 0x18; /* R18 */
184     pxTopOfStack--;
185     *pxTopOfStack = ( StackType_t ) 0x19; /* R19 */
186     pxTopOfStack--;
187     *pxTopOfStack = ( StackType_t ) 0x20; /* R20 */
188     pxTopOfStack--;
189     *pxTopOfStack = ( StackType_t ) 0x21; /* R21 */
190     pxTopOfStack--;
191     *pxTopOfStack = ( StackType_t ) 0x22; /* R22 */
192     pxTopOfStack--;
193     *pxTopOfStack = ( StackType_t ) 0x23; /* R23 */
194     pxTopOfStack--;
195     *pxTopOfStack = ( StackType_t ) 0x24; /* R24 */
196     pxTopOfStack--;
197     *pxTopOfStack = ( StackType_t ) 0x25; /* R25 */
198     pxTopOfStack--;
199     *pxTopOfStack = ( StackType_t ) 0x26; /* R26 X */
200     pxTopOfStack--;
201     *pxTopOfStack = ( StackType_t ) 0x27; /* R27 */
202     pxTopOfStack--;
203
204     /* The Y register is not stored as it is used as the software stack and
205      * gets saved into the task control block. */
206
207     *pxTopOfStack = ( StackType_t ) 0x30;  /* R30 Z */
208     pxTopOfStack--;
209     *pxTopOfStack = ( StackType_t ) 0x031; /* R31 */
210
211     pxTopOfStack--;
212     *pxTopOfStack = portNO_CRITICAL_NESTING; /* Critical nesting is zero when the task starts. */
213
214     /*lint +e950 +e611 +e923 */
215
216     return pxTopOfStack;
217 }
218 /*-----------------------------------------------------------*/
219
220 BaseType_t xPortStartScheduler( void )
221 {
222     /* Setup the hardware to generate the tick. */
223     prvSetupTimerInterrupt();
224
225     /* Restore the context of the first task that is going to run.
226      * Normally we would just call portRESTORE_CONTEXT() here, but as the IAR
227      * compiler does not fully support inline assembler we have to make a call.*/
228     vPortStart();
229
230     /* Should not get here. */
231     return pdTRUE;
232 }
233 /*-----------------------------------------------------------*/
234
235 void vPortEndScheduler( void )
236 {
237     /* vPortEndScheduler is not implemented in this port. */
238 }
239
240 /*-----------------------------------------------------------*/
241
242 /*
243  * Setup timer to generate a tick interrupt.
244  */
245 static void prvSetupTimerInterrupt( void )
246 {
247     TICK_init();
248 }
249
250 /*-----------------------------------------------------------*/
251
252 #if configUSE_PREEMPTION == 1
253
254 /*
255  * Tick ISR for preemptive scheduler.  We can use a naked attribute as
256  * the context is saved at the start of vPortYieldFromTick().  The tick
257  * count is incremented after the context is saved.
258  */
259
260     __task void TICK_INT( void )
261     {
262         vPortYieldFromTick();
263         asm ( "reti" );
264     }
265 #else
266
267 /*
268  * Tick ISR for the cooperative scheduler.  All this does is increment the
269  * tick count.  We don't need to switch context, this can only be done by
270  * manual calls to taskYIELD();
271  */
272
273     __interrupt void TICK_INT( void )
274     {
275         /* Clear tick interrupt flag. */
276         INT_FLAGS = INT_MASK;
277
278         xTaskIncrementTick();
279     }
280 #endif /* if configUSE_PREEMPTION == 1 */
281
282 /*-----------------------------------------------------------*/
283
284 void vPortEnterCritical( void )
285 {
286     portDISABLE_INTERRUPTS();
287     uxCriticalNesting++;
288 }
289
290 /*-----------------------------------------------------------*/
291
292 void vPortExitCritical( void )
293 {
294     uxCriticalNesting--;
295
296     if( uxCriticalNesting == portNO_CRITICAL_NESTING )
297     {
298         portENABLE_INTERRUPTS();
299     }
300 }