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