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 AND BSD-3-Clause
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 /*This file has been prepared for Doxygen automatic documentation generation.*/
30 
31 /*! \file *********************************************************************
32  *
33  * \brief FreeRTOS port source for AVR32 UC3.
34  *
35  * - Compiler:           GNU GCC for AVR32
36  * - Supported devices:  All AVR32 devices can be used.
37  * - AppNote:
38  *
39  * \author               Atmel Corporation (Now Microchip):
40  *                                        https://www.microchip.com \n
41  *                       Support and FAQ: https://www.microchip.com/support/
42  *
43  *****************************************************************************/
44 
45 /*
46  * Copyright (c) 2007, Atmel Corporation All rights reserved.
47  *
48  * Redistribution and use in source and binary forms, with or without
49  * modification, are permitted provided that the following conditions are met:
50  *
51  * 1. Redistributions of source code must retain the above copyright notice,
52  * this list of conditions and the following disclaimer.
53  *
54  * 2. Redistributions in binary form must reproduce the above copyright notice,
55  * this list of conditions and the following disclaimer in the documentation
56  * and/or other materials provided with the distribution.
57  *
58  * 3. The name of ATMEL may not be used to endorse or promote products derived
59  * from this software without specific prior written permission.
60  *
61  * THIS SOFTWARE IS PROVIDED BY ATMEL ``AS IS'' AND ANY EXPRESS OR IMPLIED
62  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
63  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND
64  * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT,
65  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
66  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
67  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
68  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
69  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
70  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
71  */
72 
73 /* Standard includes. */
74 #include <sys/cpu.h>
75 #include <sys/usart.h>
76 #include <malloc.h>
77 
78 /* Scheduler includes. */
79 #include "FreeRTOS.h"
80 #include "task.h"
81 
82 /* AVR32 UC3 includes. */
83 #include <avr32/io.h>
84 #include "gpio.h"
85 #if ( configTICK_USE_TC == 1 )
86     #include "tc.h"
87 #endif
88 
89 
90 /* Constants required to setup the task context. */
91 #define portINITIAL_SR             ( ( StackType_t ) 0x00400000 ) /* AVR32 : [M2:M0]=001 I1M=0 I0M=0, GM=0 */
92 #define portINSTRUCTION_SIZE       ( ( StackType_t ) 0 )
93 
94 /* Each task maintains its own critical nesting variable. */
95 #define portNO_CRITICAL_NESTING    ( ( uint32_t ) 0 )
96 volatile uint32_t ulCriticalNesting = 9999UL;
97 
98 #if ( configTICK_USE_TC == 0 )
99     static void prvScheduleNextTick( void );
100 #else
101     static void prvClearTcInt( void );
102 #endif
103 
104 /* Setup the timer to generate the tick interrupts. */
105 static void prvSetupTimerInterrupt( void );
106 
107 /*-----------------------------------------------------------*/
108 
109 /*
110  * Low-level initialization routine called during startup, before the main
111  * function.
112  * This version comes in replacement to the default one provided by Newlib.
113  * Newlib's _init_startup only calls init_exceptions, but Newlib's exception
114  * vectors are not compatible with the SCALL management in the current FreeRTOS
115  * port. More low-level initializations are besides added here.
116  */
_init_startup(void)117 void _init_startup( void )
118 {
119     /* Import the Exception Vector Base Address. */
120     extern void _evba;
121 
122     #if configHEAP_INIT
123         extern void __heap_start__;
124         extern void __heap_end__;
125         BaseType_t * pxMem;
126     #endif
127 
128     /* Load the Exception Vector Base Address in the corresponding system register. */
129     Set_system_register( AVR32_EVBA, ( int ) &_evba );
130 
131     /* Enable exceptions. */
132     ENABLE_ALL_EXCEPTIONS();
133 
134     /* Initialize interrupt handling. */
135     INTC_init_interrupts();
136 
137     #if configHEAP_INIT
138         /* Initialize the heap used by malloc. */
139         for( pxMem = &__heap_start__; pxMem < ( BaseType_t * ) &__heap_end__; )
140         {
141             *pxMem++ = 0xA5A5A5A5;
142         }
143     #endif
144 
145     /* Give the used CPU clock frequency to Newlib, so it can work properly. */
146     set_cpu_hz( configCPU_CLOCK_HZ );
147 
148     /* Code section present if and only if the debug trace is activated. */
149     #if configDBG
150     {
151         static const gpio_map_t DBG_USART_GPIO_MAP =
152         {
153             { configDBG_USART_RX_PIN, configDBG_USART_RX_FUNCTION },
154             { configDBG_USART_TX_PIN, configDBG_USART_TX_FUNCTION }
155         };
156 
157         /* Initialize the USART used for the debug trace with the configured parameters. */
158         set_usart_base( ( void * ) configDBG_USART );
159         gpio_enable_module( DBG_USART_GPIO_MAP,
160                             sizeof( DBG_USART_GPIO_MAP ) / sizeof( DBG_USART_GPIO_MAP[ 0 ] ) );
161         usart_init( configDBG_USART_BAUDRATE );
162     }
163     #endif /* if configDBG */
164 }
165 /*-----------------------------------------------------------*/
166 
167 /*
168  * malloc, realloc and free are meant to be called through respectively
169  * pvPortMalloc, pvPortRealloc and vPortFree.
170  * The latter functions call the former ones from within sections where tasks
171  * are suspended, so the latter functions are task-safe. __malloc_lock and
172  * __malloc_unlock use the same mechanism to also keep the former functions
173  * task-safe as they may be called directly from Newlib's functions.
174  * However, all these functions are interrupt-unsafe and SHALL THEREFORE NOT BE
175  * CALLED FROM WITHIN AN INTERRUPT, because __malloc_lock and __malloc_unlock do
176  * not call portENTER_CRITICAL and portEXIT_CRITICAL in order not to disable
177  * interrupts during memory allocation management as this may be a very time-
178  * consuming process.
179  */
180 
181 /*
182  * Lock routine called by Newlib on malloc / realloc / free entry to guarantee a
183  * safe section as memory allocation management uses global data.
184  * See the aforementioned details.
185  */
__malloc_lock(struct _reent * ptr)186 void __malloc_lock( struct _reent * ptr )
187 {
188     vTaskSuspendAll();
189 }
190 
191 /*
192  * Unlock routine called by Newlib on malloc / realloc / free exit to guarantee
193  * a safe section as memory allocation management uses global data.
194  * See the aforementioned details.
195  */
__malloc_unlock(struct _reent * ptr)196 void __malloc_unlock( struct _reent * ptr )
197 {
198     xTaskResumeAll();
199 }
200 /*-----------------------------------------------------------*/
201 
202 /* Added as there is no such function in FreeRTOS. */
pvPortRealloc(void * pv,size_t xWantedSize)203 void * pvPortRealloc( void * pv,
204                       size_t xWantedSize )
205 {
206     void * pvReturn;
207 
208     vTaskSuspendAll();
209     {
210         pvReturn = realloc( pv, xWantedSize );
211     }
212     xTaskResumeAll();
213 
214     return pvReturn;
215 }
216 /*-----------------------------------------------------------*/
217 
218 /* The cooperative scheduler requires a normal IRQ service routine to
219  * simply increment the system tick. */
220 
221 /* The preemptive scheduler is defined as "naked" as the full context is saved
222  * on entry as part of the context switch. */
vTick(void)223 __attribute__( ( __naked__ ) ) static void vTick( void )
224 {
225     /* Save the context of the interrupted task. */
226     portSAVE_CONTEXT_OS_INT();
227 
228     #if ( configTICK_USE_TC == 1 )
229         /* Clear the interrupt flag. */
230         prvClearTcInt();
231     #else
232 
233         /* Schedule the COUNT&COMPARE match interrupt in (configCPU_CLOCK_HZ/configTICK_RATE_HZ)
234          * clock cycles from now. */
235         prvScheduleNextTick();
236     #endif
237 
238     /* Because FreeRTOS is not supposed to run with nested interrupts, put all OS
239      * calls in a critical section . */
240     portENTER_CRITICAL();
241     xTaskIncrementTick();
242     portEXIT_CRITICAL();
243 
244     /* Restore the context of the "elected task". */
245     portRESTORE_CONTEXT_OS_INT();
246 }
247 /*-----------------------------------------------------------*/
248 
SCALLYield(void)249 __attribute__( ( __naked__ ) ) void SCALLYield( void )
250 {
251     /* Save the context of the interrupted task. */
252     portSAVE_CONTEXT_SCALL();
253     vTaskSwitchContext();
254     portRESTORE_CONTEXT_SCALL();
255 }
256 /*-----------------------------------------------------------*/
257 
258 /* The code generated by the GCC compiler uses the stack in different ways at
259  * different optimisation levels.  The interrupt flags can therefore not always
260  * be saved to the stack.  Instead the critical section nesting level is stored
261  * in a variable, which is then saved as part of the stack context. */
vPortEnterCritical(void)262 __attribute__( ( __noinline__ ) ) void vPortEnterCritical( void )
263 {
264     /* Disable interrupts */
265     portDISABLE_INTERRUPTS();
266 
267     /* Now that interrupts are disabled, ulCriticalNesting can be accessed
268      * directly.  Increment ulCriticalNesting to keep a count of how many times
269      * portENTER_CRITICAL() has been called. */
270     ulCriticalNesting++;
271 }
272 /*-----------------------------------------------------------*/
273 
vPortExitCritical(void)274 __attribute__( ( __noinline__ ) ) void vPortExitCritical( void )
275 {
276     if( ulCriticalNesting > portNO_CRITICAL_NESTING )
277     {
278         ulCriticalNesting--;
279 
280         if( ulCriticalNesting == portNO_CRITICAL_NESTING )
281         {
282             /* Enable all interrupt/exception. */
283             portENABLE_INTERRUPTS();
284         }
285     }
286 }
287 /*-----------------------------------------------------------*/
288 
289 /*
290  * Initialise the stack of a task to look exactly as if a call to
291  * portSAVE_CONTEXT had been called.
292  *
293  * See header file for description.
294  */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)295 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
296                                      TaskFunction_t pxCode,
297                                      void * pvParameters )
298 {
299     /* Setup the initial stack of the task.  The stack is set exactly as
300      * expected by the portRESTORE_CONTEXT() macro. */
301 
302     /* When the task starts, it will expect to find the function parameter in R12. */
303     pxTopOfStack--;
304     *pxTopOfStack-- = ( StackType_t ) 0x08080808;                    /* R8 */
305     *pxTopOfStack-- = ( StackType_t ) 0x09090909;                    /* R9 */
306     *pxTopOfStack-- = ( StackType_t ) 0x0A0A0A0A;                    /* R10 */
307     *pxTopOfStack-- = ( StackType_t ) 0x0B0B0B0B;                    /* R11 */
308     *pxTopOfStack-- = ( StackType_t ) pvParameters;                  /* R12 */
309     *pxTopOfStack-- = ( StackType_t ) 0xDEADBEEF;                    /* R14/LR */
310     *pxTopOfStack-- = ( StackType_t ) pxCode + portINSTRUCTION_SIZE; /* R15/PC */
311     *pxTopOfStack-- = ( StackType_t ) portINITIAL_SR;                /* SR */
312     *pxTopOfStack-- = ( StackType_t ) 0xFF0000FF;                    /* R0 */
313     *pxTopOfStack-- = ( StackType_t ) 0x01010101;                    /* R1 */
314     *pxTopOfStack-- = ( StackType_t ) 0x02020202;                    /* R2 */
315     *pxTopOfStack-- = ( StackType_t ) 0x03030303;                    /* R3 */
316     *pxTopOfStack-- = ( StackType_t ) 0x04040404;                    /* R4 */
317     *pxTopOfStack-- = ( StackType_t ) 0x05050505;                    /* R5 */
318     *pxTopOfStack-- = ( StackType_t ) 0x06060606;                    /* R6 */
319     *pxTopOfStack-- = ( StackType_t ) 0x07070707;                    /* R7 */
320     *pxTopOfStack = ( StackType_t ) portNO_CRITICAL_NESTING;         /* ulCriticalNesting */
321 
322     return pxTopOfStack;
323 }
324 /*-----------------------------------------------------------*/
325 
xPortStartScheduler(void)326 BaseType_t xPortStartScheduler( void )
327 {
328     /* Start the timer that generates the tick ISR.  Interrupts are disabled
329      * here already. */
330     prvSetupTimerInterrupt();
331 
332     /* Start the first task. */
333     portRESTORE_CONTEXT();
334 
335     /* Should not get here! */
336     return 0;
337 }
338 /*-----------------------------------------------------------*/
339 
vPortEndScheduler(void)340 void vPortEndScheduler( void )
341 {
342     /* It is unlikely that the AVR32 port will require this function as there
343      * is nothing to return to.  */
344 }
345 /*-----------------------------------------------------------*/
346 
347 /* Schedule the COUNT&COMPARE match interrupt in (configCPU_CLOCK_HZ/configTICK_RATE_HZ)
348  * clock cycles from now. */
349 #if ( configTICK_USE_TC == 0 )
prvScheduleFirstTick(void)350     static void prvScheduleFirstTick( void )
351     {
352         uint32_t lCycles;
353 
354         lCycles = Get_system_register( AVR32_COUNT );
355         lCycles += ( configCPU_CLOCK_HZ / configTICK_RATE_HZ );
356 
357         /* If lCycles ends up to be 0, make it 1 so that the COMPARE and exception */
358         /* generation feature does not get disabled. */
359         if( 0 == lCycles )
360         {
361             lCycles++;
362         }
363 
364         Set_system_register( AVR32_COMPARE, lCycles );
365     }
366 
prvScheduleNextTick(void)367     __attribute__( ( __noinline__ ) ) static void prvScheduleNextTick( void )
368     {
369         uint32_t lCycles, lCount;
370 
371         lCycles = Get_system_register( AVR32_COMPARE );
372         lCycles += ( configCPU_CLOCK_HZ / configTICK_RATE_HZ );
373 
374         /* If lCycles ends up to be 0, make it 1 so that the COMPARE and exception */
375         /* generation feature does not get disabled. */
376         if( 0 == lCycles )
377         {
378             lCycles++;
379         }
380 
381         lCount = Get_system_register( AVR32_COUNT );
382 
383         if( lCycles < lCount )
384         { /* We missed a tick, recover for the next. */
385             lCycles += ( configCPU_CLOCK_HZ / configTICK_RATE_HZ );
386         }
387 
388         Set_system_register( AVR32_COMPARE, lCycles );
389     }
390 #else /* if ( configTICK_USE_TC == 0 ) */
prvClearTcInt(void)391     __attribute__( ( __noinline__ ) ) static void prvClearTcInt( void )
392     {
393         AVR32_TC.channel[ configTICK_TC_CHANNEL ].sr;
394     }
395 #endif /* if ( configTICK_USE_TC == 0 ) */
396 /*-----------------------------------------------------------*/
397 
398 /* Setup the timer to generate the tick interrupts. */
prvSetupTimerInterrupt(void)399 static void prvSetupTimerInterrupt( void )
400 {
401     #if ( configTICK_USE_TC == 1 )
402         volatile avr32_tc_t * tc = &AVR32_TC;
403 
404         /* Options for waveform genration. */
405         tc_waveform_opt_t waveform_opt =
406         {
407             .channel = configTICK_TC_CHANNEL,              /* Channel selection. */
408 
409             .bswtrg  = TC_EVT_EFFECT_NOOP,                 /* Software trigger effect on TIOB. */
410             .beevt   = TC_EVT_EFFECT_NOOP,                 /* External event effect on TIOB. */
411             .bcpc    = TC_EVT_EFFECT_NOOP,                 /* RC compare effect on TIOB. */
412             .bcpb    = TC_EVT_EFFECT_NOOP,                 /* RB compare effect on TIOB. */
413 
414             .aswtrg  = TC_EVT_EFFECT_NOOP,                 /* Software trigger effect on TIOA. */
415             .aeevt   = TC_EVT_EFFECT_NOOP,                 /* External event effect on TIOA. */
416             .acpc    = TC_EVT_EFFECT_NOOP,                 /* RC compare effect on TIOA: toggle. */
417             .acpa    = TC_EVT_EFFECT_NOOP,                 /* RA compare effect on TIOA: toggle (other possibilities are none, set and clear). */
418 
419             .wavsel  = TC_WAVEFORM_SEL_UP_MODE_RC_TRIGGER, /* Waveform selection: Up mode without automatic trigger on RC compare. */
420             .enetrg  = FALSE,                              /* External event trigger enable. */
421             .eevt    = 0,                                  /* External event selection. */
422             .eevtedg = TC_SEL_NO_EDGE,                     /* External event edge selection. */
423             .cpcdis  = FALSE,                              /* Counter disable when RC compare. */
424             .cpcstop = FALSE,                              /* Counter clock stopped with RC compare. */
425 
426             .burst   = FALSE,                              /* Burst signal selection. */
427             .clki    = FALSE,                              /* Clock inversion. */
428             .tcclks  = TC_CLOCK_SOURCE_TC2                 /* Internal source clock 2. */
429         };
430 
431         tc_interrupt_t tc_interrupt =
432         {
433             .etrgs = 0,
434             .ldrbs = 0,
435             .ldras = 0,
436             .cpcs  = 1,
437             .cpbs  = 0,
438             .cpas  = 0,
439             .lovrs = 0,
440             .covfs = 0,
441         };
442     #endif /* if ( configTICK_USE_TC == 1 ) */
443 
444     /* Disable all interrupt/exception. */
445     portDISABLE_INTERRUPTS();
446 
447     /* Register the compare interrupt handler to the interrupt controller and
448      * enable the compare interrupt. */
449 
450     #if ( configTICK_USE_TC == 1 )
451     {
452         INTC_register_interrupt( &vTick, configTICK_TC_IRQ, INT0 );
453 
454         /* Initialize the timer/counter. */
455         tc_init_waveform( tc, &waveform_opt );
456 
457         /* Set the compare triggers.
458          * Remember TC counter is 16-bits, so counting second is not possible!
459          * That's why we configure it to count ms. */
460         tc_write_rc( tc, configTICK_TC_CHANNEL, ( configPBA_CLOCK_HZ / 4 ) / configTICK_RATE_HZ );
461 
462         tc_configure_interrupts( tc, configTICK_TC_CHANNEL, &tc_interrupt );
463 
464         /* Start the timer/counter. */
465         tc_start( tc, configTICK_TC_CHANNEL );
466     }
467     #else /* if ( configTICK_USE_TC == 1 ) */
468     {
469         INTC_register_interrupt( &vTick, AVR32_CORE_COMPARE_IRQ, INT0 );
470         prvScheduleFirstTick();
471     }
472     #endif /* if ( configTICK_USE_TC == 1 ) */
473 }
474