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  *
31  * Changes from V2.6.0
32  *
33  + AVR port - Replaced the inb() and outb() functions with direct memory
34  +    access.  This allows the port to be built with the 20050414 build of
35  +    WinAVR.
36  */
37 
38 #include <stdlib.h>
39 #include <avr/interrupt.h>
40 
41 #include "FreeRTOS.h"
42 #include "task.h"
43 
44 /*-----------------------------------------------------------
45 * Implementation of functions defined in portable.h for the AVR port.
46 *----------------------------------------------------------*/
47 
48 /* Start tasks with interrupts enables. */
49 #define portFLAGS_INT_ENABLED                   ( ( StackType_t ) 0x80 )
50 
51 /* Hardware constants for timer 1. */
52 #define portCLEAR_COUNTER_ON_MATCH              ( ( uint8_t ) 0x08 )
53 #define portPRESCALE_64                         ( ( uint8_t ) 0x03 )
54 #define portCLOCK_PRESCALER                     ( ( uint32_t ) 64 )
55 #define portCOMPARE_MATCH_A_INTERRUPT_ENABLE    ( ( uint8_t ) 0x10 )
56 
57 /*-----------------------------------------------------------*/
58 
59 /* We require the address of the pxCurrentTCB variable, but don't want to know
60  * any details of its type. */
61 typedef void TCB_t;
62 extern volatile TCB_t * volatile pxCurrentTCB;
63 
64 /*-----------------------------------------------------------*/
65 
66 /*
67  * Macro to save all the general purpose registers, the save the stack pointer
68  * into the TCB.
69  *
70  * The first thing we do is save the flags then disable interrupts.  This is to
71  * guard our stack against having a context switch interrupt after we have already
72  * pushed the registers onto the stack - causing the 32 registers to be on the
73  * stack twice.
74  *
75  * r1 is set to zero as the compiler expects it to be thus, however some
76  * of the math routines make use of R1.
77  *
78  * The interrupts will have been disabled during the call to portSAVE_CONTEXT()
79  * so we need not worry about reading/writing to the stack pointer.
80  */
81 
82 #define portSAVE_CONTEXT()                               \
83     asm volatile ( "push   r0                      \n\t" \
84                    "in     r0, __SREG__            \n\t" \
85                    "cli                            \n\t" \
86                    "push   r0                      \n\t" \
87                    "push   r1                      \n\t" \
88                    "clr    r1                      \n\t" \
89                    "push   r2                      \n\t" \
90                    "push   r3                      \n\t" \
91                    "push   r4                      \n\t" \
92                    "push   r5                      \n\t" \
93                    "push   r6                      \n\t" \
94                    "push   r7                      \n\t" \
95                    "push   r8                      \n\t" \
96                    "push   r9                      \n\t" \
97                    "push   r10                     \n\t" \
98                    "push   r11                     \n\t" \
99                    "push   r12                     \n\t" \
100                    "push   r13                     \n\t" \
101                    "push   r14                     \n\t" \
102                    "push   r15                     \n\t" \
103                    "push   r16                     \n\t" \
104                    "push   r17                     \n\t" \
105                    "push   r18                     \n\t" \
106                    "push   r19                     \n\t" \
107                    "push   r20                     \n\t" \
108                    "push   r21                     \n\t" \
109                    "push   r22                     \n\t" \
110                    "push   r23                     \n\t" \
111                    "push   r24                     \n\t" \
112                    "push   r25                     \n\t" \
113                    "push   r26                     \n\t" \
114                    "push   r27                     \n\t" \
115                    "push   r28                     \n\t" \
116                    "push   r29                     \n\t" \
117                    "push   r30                     \n\t" \
118                    "push   r31                     \n\t" \
119                    "lds    r26, pxCurrentTCB       \n\t" \
120                    "lds    r27, pxCurrentTCB + 1   \n\t" \
121                    "in     r0, 0x3d                \n\t" \
122                    "st     x+, r0                  \n\t" \
123                    "in     r0, 0x3e                \n\t" \
124                    "st     x+, r0                  \n\t" \
125                    );
126 
127 /*
128  * Opposite to portSAVE_CONTEXT().  Interrupts will have been disabled during
129  * the context save so we can write to the stack pointer.
130  */
131 
132 #define portRESTORE_CONTEXT()                            \
133     asm volatile ( "lds    r26, pxCurrentTCB       \n\t" \
134                    "lds    r27, pxCurrentTCB + 1   \n\t" \
135                    "ld     r28, x+                 \n\t" \
136                    "out    __SP_L__, r28           \n\t" \
137                    "ld     r29, x+                 \n\t" \
138                    "out    __SP_H__, r29           \n\t" \
139                    "pop    r31                     \n\t" \
140                    "pop    r30                     \n\t" \
141                    "pop    r29                     \n\t" \
142                    "pop    r28                     \n\t" \
143                    "pop    r27                     \n\t" \
144                    "pop    r26                     \n\t" \
145                    "pop    r25                     \n\t" \
146                    "pop    r24                     \n\t" \
147                    "pop    r23                     \n\t" \
148                    "pop    r22                     \n\t" \
149                    "pop    r21                     \n\t" \
150                    "pop    r20                     \n\t" \
151                    "pop    r19                     \n\t" \
152                    "pop    r18                     \n\t" \
153                    "pop    r17                     \n\t" \
154                    "pop    r16                     \n\t" \
155                    "pop    r15                     \n\t" \
156                    "pop    r14                     \n\t" \
157                    "pop    r13                     \n\t" \
158                    "pop    r12                     \n\t" \
159                    "pop    r11                     \n\t" \
160                    "pop    r10                     \n\t" \
161                    "pop    r9                      \n\t" \
162                    "pop    r8                      \n\t" \
163                    "pop    r7                      \n\t" \
164                    "pop    r6                      \n\t" \
165                    "pop    r5                      \n\t" \
166                    "pop    r4                      \n\t" \
167                    "pop    r3                      \n\t" \
168                    "pop    r2                      \n\t" \
169                    "pop    r1                      \n\t" \
170                    "pop    r0                      \n\t" \
171                    "out    __SREG__, r0            \n\t" \
172                    "pop    r0                      \n\t" \
173                    );
174 
175 /*-----------------------------------------------------------*/
176 
177 /*
178  * Perform hardware setup to enable ticks from timer 1, compare match A.
179  */
180 static void prvSetupTimerInterrupt( void );
181 /*-----------------------------------------------------------*/
182 
183 /*
184  * See header file for description.
185  */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)186 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
187                                      TaskFunction_t pxCode,
188                                      void * pvParameters )
189 {
190     uint16_t usAddress;
191 
192     /* Place a few bytes of known values on the bottom of the stack.
193      * This is just useful for debugging. */
194 
195     *pxTopOfStack = 0x11;
196     pxTopOfStack--;
197     *pxTopOfStack = 0x22;
198     pxTopOfStack--;
199     *pxTopOfStack = 0x33;
200     pxTopOfStack--;
201 
202     /* Simulate how the stack would look after a call to vPortYield() generated by
203      * the compiler. */
204 
205     /*lint -e950 -e611 -e923 Lint doesn't like this much - but nothing I can do about it. */
206 
207     /* The start of the task code will be popped off the stack last, so place
208      * it on first. */
209     usAddress = ( uint16_t ) pxCode;
210     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
211     pxTopOfStack--;
212 
213     usAddress >>= 8;
214     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
215     pxTopOfStack--;
216 
217     /* Next simulate the stack as if after a call to portSAVE_CONTEXT().
218      *  portSAVE_CONTEXT places the flags on the stack immediately after r0
219      *  to ensure the interrupts get disabled as soon as possible, and so ensuring
220      *  the stack use is minimal should a context switch interrupt occur. */
221     *pxTopOfStack = ( StackType_t ) 0x00; /* R0 */
222     pxTopOfStack--;
223     *pxTopOfStack = portFLAGS_INT_ENABLED;
224     pxTopOfStack--;
225 
226 
227     /* Now the remaining registers.   The compiler expects R1 to be 0. */
228     *pxTopOfStack = ( StackType_t ) 0x00; /* R1 */
229     pxTopOfStack--;
230     *pxTopOfStack = ( StackType_t ) 0x02; /* R2 */
231     pxTopOfStack--;
232     *pxTopOfStack = ( StackType_t ) 0x03; /* R3 */
233     pxTopOfStack--;
234     *pxTopOfStack = ( StackType_t ) 0x04; /* R4 */
235     pxTopOfStack--;
236     *pxTopOfStack = ( StackType_t ) 0x05; /* R5 */
237     pxTopOfStack--;
238     *pxTopOfStack = ( StackType_t ) 0x06; /* R6 */
239     pxTopOfStack--;
240     *pxTopOfStack = ( StackType_t ) 0x07; /* R7 */
241     pxTopOfStack--;
242     *pxTopOfStack = ( StackType_t ) 0x08; /* R8 */
243     pxTopOfStack--;
244     *pxTopOfStack = ( StackType_t ) 0x09; /* R9 */
245     pxTopOfStack--;
246     *pxTopOfStack = ( StackType_t ) 0x10; /* R10 */
247     pxTopOfStack--;
248     *pxTopOfStack = ( StackType_t ) 0x11; /* R11 */
249     pxTopOfStack--;
250     *pxTopOfStack = ( StackType_t ) 0x12; /* R12 */
251     pxTopOfStack--;
252     *pxTopOfStack = ( StackType_t ) 0x13; /* R13 */
253     pxTopOfStack--;
254     *pxTopOfStack = ( StackType_t ) 0x14; /* R14 */
255     pxTopOfStack--;
256     *pxTopOfStack = ( StackType_t ) 0x15; /* R15 */
257     pxTopOfStack--;
258     *pxTopOfStack = ( StackType_t ) 0x16; /* R16 */
259     pxTopOfStack--;
260     *pxTopOfStack = ( StackType_t ) 0x17; /* R17 */
261     pxTopOfStack--;
262     *pxTopOfStack = ( StackType_t ) 0x18; /* R18 */
263     pxTopOfStack--;
264     *pxTopOfStack = ( StackType_t ) 0x19; /* R19 */
265     pxTopOfStack--;
266     *pxTopOfStack = ( StackType_t ) 0x20; /* R20 */
267     pxTopOfStack--;
268     *pxTopOfStack = ( StackType_t ) 0x21; /* R21 */
269     pxTopOfStack--;
270     *pxTopOfStack = ( StackType_t ) 0x22; /* R22 */
271     pxTopOfStack--;
272     *pxTopOfStack = ( StackType_t ) 0x23; /* R23 */
273     pxTopOfStack--;
274 
275     /* Place the parameter on the stack in the expected location. */
276     usAddress = ( uint16_t ) pvParameters;
277     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
278     pxTopOfStack--;
279 
280     usAddress >>= 8;
281     *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
282     pxTopOfStack--;
283 
284     *pxTopOfStack = ( StackType_t ) 0x26;  /* R26 X */
285     pxTopOfStack--;
286     *pxTopOfStack = ( StackType_t ) 0x27;  /* R27 */
287     pxTopOfStack--;
288     *pxTopOfStack = ( StackType_t ) 0x28;  /* R28 Y */
289     pxTopOfStack--;
290     *pxTopOfStack = ( StackType_t ) 0x29;  /* R29 */
291     pxTopOfStack--;
292     *pxTopOfStack = ( StackType_t ) 0x30;  /* R30 Z */
293     pxTopOfStack--;
294     *pxTopOfStack = ( StackType_t ) 0x031; /* R31 */
295     pxTopOfStack--;
296 
297     /*lint +e950 +e611 +e923 */
298 
299     return pxTopOfStack;
300 }
301 /*-----------------------------------------------------------*/
302 
xPortStartScheduler(void)303 BaseType_t xPortStartScheduler( void )
304 {
305     /* Setup the hardware to generate the tick. */
306     prvSetupTimerInterrupt();
307 
308     /* Restore the context of the first task that is going to run. */
309     portRESTORE_CONTEXT();
310 
311     /* Simulate a function call end as generated by the compiler.  We will now
312      * jump to the start of the task the context of which we have just restored. */
313     asm volatile ( "ret" );
314 
315     /* Should not get here. */
316     return pdTRUE;
317 }
318 /*-----------------------------------------------------------*/
319 
vPortEndScheduler(void)320 void vPortEndScheduler( void )
321 {
322     /* It is unlikely that the AVR port will get stopped.  If required simply
323      * disable the tick interrupt here. */
324 }
325 /*-----------------------------------------------------------*/
326 
327 /*
328  * Manual context switch.  The first thing we do is save the registers so we
329  * can use a naked attribute.
330  */
331 void vPortYield( void ) __attribute__( ( naked ) );
vPortYield(void)332 void vPortYield( void )
333 {
334     portSAVE_CONTEXT();
335     vTaskSwitchContext();
336     portRESTORE_CONTEXT();
337 
338     asm volatile ( "ret" );
339 }
340 /*-----------------------------------------------------------*/
341 
342 /*
343  * Context switch function used by the tick.  This must be identical to
344  * vPortYield() from the call to vTaskSwitchContext() onwards.  The only
345  * difference from vPortYield() is the tick count is incremented as the
346  * call comes from the tick ISR.
347  */
348 void vPortYieldFromTick( void ) __attribute__( ( naked ) );
vPortYieldFromTick(void)349 void vPortYieldFromTick( void )
350 {
351     portSAVE_CONTEXT();
352 
353     if( xTaskIncrementTick() != pdFALSE )
354     {
355         vTaskSwitchContext();
356     }
357 
358     portRESTORE_CONTEXT();
359 
360     asm volatile ( "ret" );
361 }
362 /*-----------------------------------------------------------*/
363 
364 /*
365  * Setup timer 1 compare match A to generate a tick interrupt.
366  */
prvSetupTimerInterrupt(void)367 static void prvSetupTimerInterrupt( void )
368 {
369     uint32_t ulCompareMatch;
370     uint8_t ucHighByte, ucLowByte;
371 
372     /* Using 16bit timer 1 to generate the tick.  Correct fuses must be
373      * selected for the configCPU_CLOCK_HZ clock. */
374 
375     ulCompareMatch = configCPU_CLOCK_HZ / configTICK_RATE_HZ;
376 
377     /* We only have 16 bits so have to scale to get our required tick rate. */
378     ulCompareMatch /= portCLOCK_PRESCALER;
379 
380     /* Adjust for correct value. */
381     ulCompareMatch -= ( uint32_t ) 1;
382 
383     /* Setup compare match value for compare match A.  Interrupts are disabled
384      * before this is called so we need not worry here. */
385     ucLowByte = ( uint8_t ) ( ulCompareMatch & ( uint32_t ) 0xff );
386     ulCompareMatch >>= 8;
387     ucHighByte = ( uint8_t ) ( ulCompareMatch & ( uint32_t ) 0xff );
388     OCR1AH = ucHighByte;
389     OCR1AL = ucLowByte;
390 
391     /* Setup clock source and compare match behaviour. */
392     ucLowByte = portCLEAR_COUNTER_ON_MATCH | portPRESCALE_64;
393     TCCR1B = ucLowByte;
394 
395     /* Enable the interrupt - this is okay as interrupt are currently globally
396      * disabled. */
397     ucLowByte = TIMSK;
398     ucLowByte |= portCOMPARE_MATCH_A_INTERRUPT_ENABLE;
399     TIMSK = ucLowByte;
400 }
401 /*-----------------------------------------------------------*/
402 
403 #if configUSE_PREEMPTION == 1
404 
405 /*
406  * Tick ISR for preemptive scheduler.  We can use a naked attribute as
407  * the context is saved at the start of vPortYieldFromTick().  The tick
408  * count is incremented after the context is saved.
409  */
410     void TIMER1_COMPA_vect( void ) __attribute__( ( signal, naked ) );
TIMER1_COMPA_vect(void)411     void TIMER1_COMPA_vect( void )
412     {
413         vPortYieldFromTick();
414         asm volatile ( "reti" );
415     }
416 #else
417 
418 /*
419  * Tick ISR for the cooperative scheduler.  All this does is increment the
420  * tick count.  We don't need to switch context, this can only be done by
421  * manual calls to taskYIELD();
422  */
423     void TIMER1_COMPA_vect( void ) __attribute__( ( signal ) );
TIMER1_COMPA_vect(void)424     void TIMER1_COMPA_vect( void )
425     {
426         xTaskIncrementTick();
427     }
428 #endif /* if configUSE_PREEMPTION == 1 */
429