2 * FreeRTOS Kernel V11.0.1
3 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 * SPDX-License-Identifier: MIT
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:
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
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.
24 * https://www.FreeRTOS.org
25 * https://github.com/FreeRTOS
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
39 #include <avr/interrupt.h>
44 /*-----------------------------------------------------------
45 * Implementation of functions defined in portable.h for the AVR port.
46 *----------------------------------------------------------*/
48 /* Start tasks with interrupts enables. */
49 #define portFLAGS_INT_ENABLED ( ( StackType_t ) 0x80 )
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 )
57 /*-----------------------------------------------------------*/
59 /* We require the address of the pxCurrentTCB variable, but don't want to know
60 * any details of its type. */
62 extern volatile TCB_t * volatile pxCurrentTCB;
64 /*-----------------------------------------------------------*/
67 * Macro to save all the general purpose registers, the save the stack pointer
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
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.
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.
82 #define portSAVE_CONTEXT() \
83 asm volatile ( "push r0 \n\t" \
84 "in r0, __SREG__ \n\t" \
119 "lds r26, pxCurrentTCB \n\t" \
120 "lds r27, pxCurrentTCB + 1 \n\t" \
128 * Opposite to portSAVE_CONTEXT(). Interrupts will have been disabled during
129 * the context save so we can write to the stack pointer.
132 #define portRESTORE_CONTEXT() \
133 asm volatile ( "lds r26, pxCurrentTCB \n\t" \
134 "lds r27, pxCurrentTCB + 1 \n\t" \
136 "out __SP_L__, r28 \n\t" \
138 "out __SP_H__, r29 \n\t" \
171 "out __SREG__, r0 \n\t" \
175 /*-----------------------------------------------------------*/
178 * Perform hardware setup to enable ticks from timer 1, compare match A.
180 static void prvSetupTimerInterrupt( void );
181 /*-----------------------------------------------------------*/
184 * See header file for description.
186 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
187 TaskFunction_t pxCode,
188 void * pvParameters )
192 /* Place a few bytes of known values on the bottom of the stack.
193 * This is just useful for debugging. */
195 *pxTopOfStack = 0x11;
197 *pxTopOfStack = 0x22;
199 *pxTopOfStack = 0x33;
202 /* Simulate how the stack would look after a call to vPortYield() generated by
205 /*lint -e950 -e611 -e923 Lint doesn't like this much - but nothing I can do about it. */
207 /* The start of the task code will be popped off the stack last, so place
209 usAddress = ( uint16_t ) pxCode;
210 *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
214 *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
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 */
223 *pxTopOfStack = portFLAGS_INT_ENABLED;
227 /* Now the remaining registers. The compiler expects R1 to be 0. */
228 *pxTopOfStack = ( StackType_t ) 0x00; /* R1 */
230 *pxTopOfStack = ( StackType_t ) 0x02; /* R2 */
232 *pxTopOfStack = ( StackType_t ) 0x03; /* R3 */
234 *pxTopOfStack = ( StackType_t ) 0x04; /* R4 */
236 *pxTopOfStack = ( StackType_t ) 0x05; /* R5 */
238 *pxTopOfStack = ( StackType_t ) 0x06; /* R6 */
240 *pxTopOfStack = ( StackType_t ) 0x07; /* R7 */
242 *pxTopOfStack = ( StackType_t ) 0x08; /* R8 */
244 *pxTopOfStack = ( StackType_t ) 0x09; /* R9 */
246 *pxTopOfStack = ( StackType_t ) 0x10; /* R10 */
248 *pxTopOfStack = ( StackType_t ) 0x11; /* R11 */
250 *pxTopOfStack = ( StackType_t ) 0x12; /* R12 */
252 *pxTopOfStack = ( StackType_t ) 0x13; /* R13 */
254 *pxTopOfStack = ( StackType_t ) 0x14; /* R14 */
256 *pxTopOfStack = ( StackType_t ) 0x15; /* R15 */
258 *pxTopOfStack = ( StackType_t ) 0x16; /* R16 */
260 *pxTopOfStack = ( StackType_t ) 0x17; /* R17 */
262 *pxTopOfStack = ( StackType_t ) 0x18; /* R18 */
264 *pxTopOfStack = ( StackType_t ) 0x19; /* R19 */
266 *pxTopOfStack = ( StackType_t ) 0x20; /* R20 */
268 *pxTopOfStack = ( StackType_t ) 0x21; /* R21 */
270 *pxTopOfStack = ( StackType_t ) 0x22; /* R22 */
272 *pxTopOfStack = ( StackType_t ) 0x23; /* R23 */
275 /* Place the parameter on the stack in the expected location. */
276 usAddress = ( uint16_t ) pvParameters;
277 *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
281 *pxTopOfStack = ( StackType_t ) ( usAddress & ( uint16_t ) 0x00ff );
284 *pxTopOfStack = ( StackType_t ) 0x26; /* R26 X */
286 *pxTopOfStack = ( StackType_t ) 0x27; /* R27 */
288 *pxTopOfStack = ( StackType_t ) 0x28; /* R28 Y */
290 *pxTopOfStack = ( StackType_t ) 0x29; /* R29 */
292 *pxTopOfStack = ( StackType_t ) 0x30; /* R30 Z */
294 *pxTopOfStack = ( StackType_t ) 0x031; /* R31 */
297 /*lint +e950 +e611 +e923 */
301 /*-----------------------------------------------------------*/
303 BaseType_t xPortStartScheduler( void )
305 /* Setup the hardware to generate the tick. */
306 prvSetupTimerInterrupt();
308 /* Restore the context of the first task that is going to run. */
309 portRESTORE_CONTEXT();
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" );
315 /* Should not get here. */
318 /*-----------------------------------------------------------*/
320 void vPortEndScheduler( void )
322 /* It is unlikely that the AVR port will get stopped. If required simply
323 * disable the tick interrupt here. */
325 /*-----------------------------------------------------------*/
328 * Manual context switch. The first thing we do is save the registers so we
329 * can use a naked attribute.
331 void vPortYield( void ) __attribute__( ( naked ) );
332 void vPortYield( void )
335 vTaskSwitchContext();
336 portRESTORE_CONTEXT();
338 asm volatile ( "ret" );
340 /*-----------------------------------------------------------*/
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.
348 void vPortYieldFromTick( void ) __attribute__( ( naked ) );
349 void vPortYieldFromTick( void )
353 if( xTaskIncrementTick() != pdFALSE )
355 vTaskSwitchContext();
358 portRESTORE_CONTEXT();
360 asm volatile ( "ret" );
362 /*-----------------------------------------------------------*/
365 * Setup timer 1 compare match A to generate a tick interrupt.
367 static void prvSetupTimerInterrupt( void )
369 uint32_t ulCompareMatch;
370 uint8_t ucHighByte, ucLowByte;
372 /* Using 16bit timer 1 to generate the tick. Correct fuses must be
373 * selected for the configCPU_CLOCK_HZ clock. */
375 ulCompareMatch = configCPU_CLOCK_HZ / configTICK_RATE_HZ;
377 /* We only have 16 bits so have to scale to get our required tick rate. */
378 ulCompareMatch /= portCLOCK_PRESCALER;
380 /* Adjust for correct value. */
381 ulCompareMatch -= ( uint32_t ) 1;
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 );
391 /* Setup clock source and compare match behaviour. */
392 ucLowByte = portCLEAR_COUNTER_ON_MATCH | portPRESCALE_64;
395 /* Enable the interrupt - this is okay as interrupt are currently globally
398 ucLowByte |= portCOMPARE_MATCH_A_INTERRUPT_ENABLE;
401 /*-----------------------------------------------------------*/
403 #if configUSE_PREEMPTION == 1
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.
410 void TIMER1_COMPA_vect( void ) __attribute__( ( signal, naked ) );
411 void TIMER1_COMPA_vect( void )
413 vPortYieldFromTick();
414 asm volatile ( "reti" );
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();
423 void TIMER1_COMPA_vect( void ) __attribute__( ( signal ) );
424 void TIMER1_COMPA_vect( void )
426 xTaskIncrementTick();
428 #endif /* if configUSE_PREEMPTION == 1 */