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