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