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 * Implementation of functions defined in portable.h for the ST STR91x ARM9
31 * port.
32 *----------------------------------------------------------*/
33 
34 /* Library includes. */
35 #include "91x_lib.h"
36 
37 /* Standard includes. */
38 #include <stdlib.h>
39 #include <assert.h>
40 
41 /* Scheduler includes. */
42 #include "FreeRTOS.h"
43 #include "task.h"
44 
45 #ifndef configUSE_WATCHDOG_TICK
46     #error configUSE_WATCHDOG_TICK must be set to either 1 or 0 in FreeRTOSConfig.h to use either the Watchdog or timer 2 to generate the tick interrupt respectively.
47 #endif
48 
49 /* Constants required to setup the initial stack. */
50 #ifndef _RUN_TASK_IN_ARM_MODE_
51     #define portINITIAL_SPSR       ( ( StackType_t ) 0x3f )      /* System mode, THUMB mode, interrupts enabled. */
52 #else
53     #define portINITIAL_SPSR       ( ( StackType_t ) 0x1f )      /* System mode, ARM mode, interrupts enabled. */
54 #endif
55 
56 #define portINSTRUCTION_SIZE       ( ( StackType_t ) 4 )
57 
58 /* Constants required to handle critical sections. */
59 #define portNO_CRITICAL_NESTING    ( ( uint32_t ) 0 )
60 
61 #ifndef abs
62     #define abs( x )    ( ( x ) > 0 ? ( x ) : -( x ) )
63 #endif
64 
65 /**
66  * Toggle a led using the following algorithm:
67  * if ( GPIO_ReadBit(GPIO9, GPIO_Pin_2) )
68  * {
69  *   GPIO_WriteBit( GPIO9, GPIO_Pin_2, Bit_RESET );
70  * }
71  * else
72  * {
73  *   GPIO_WriteBit( GPIO9, GPIO_Pin_2, Bit_RESET );
74  * }
75  *
76  */
77 #define TOGGLE_LED( port, pin )                                           \
78     if( ( ( ( ( port )->DR[ ( pin ) << 2 ] ) ) & ( pin ) ) != Bit_RESET ) \
79     {                                                                     \
80         ( port )->DR[ ( pin ) << 2 ] = 0x00;                              \
81     }                                                                     \
82     else                                                                  \
83     {                                                                     \
84         ( port )->DR[ ( pin ) << 2 ] = ( pin );                           \
85     }
86 
87 
88 /*-----------------------------------------------------------*/
89 
90 /* Setup the watchdog to generate the tick interrupts. */
91 static void prvSetupTimerInterrupt( void );
92 
93 /* ulCriticalNesting will get set to zero when the first task starts.  It
94  * cannot be initialised to 0 as this will cause interrupts to be enabled
95  * during the kernel initialisation process. */
96 uint32_t ulCriticalNesting = ( uint32_t ) 9999;
97 
98 /* Tick interrupt routines for cooperative and preemptive operation
99  * respectively.  The preemptive version is not defined as __irq as it is called
100  * from an asm wrapper function. */
101 void WDG_IRQHandler( void );
102 
103 /* VIC interrupt default handler. */
104 static void prvDefaultHandler( void );
105 
106 #if configUSE_WATCHDOG_TICK == 0
107     /* Used to update the OCR timer register */
108     static u16 s_nPulseLength;
109 #endif
110 
111 /*-----------------------------------------------------------*/
112 
113 /*
114  * Initialise the stack of a task to look exactly as if a call to
115  * portSAVE_CONTEXT had been called.
116  *
117  * See header file for description.
118  */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)119 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
120                                      TaskFunction_t pxCode,
121                                      void * pvParameters )
122 {
123     StackType_t * pxOriginalTOS;
124 
125     pxOriginalTOS = pxTopOfStack;
126 
127     /* To ensure asserts in tasks.c don't fail, although in this case the assert
128      * is not really required. */
129     pxTopOfStack--;
130 
131     /* Setup the initial stack of the task.  The stack is set exactly as
132      * expected by the portRESTORE_CONTEXT() macro. */
133 
134     /* First on the stack is the return address - which in this case is the
135      * start of the task.  The offset is added to make the return address appear
136      * as it would within an IRQ ISR. */
137     *pxTopOfStack = ( StackType_t ) pxCode + portINSTRUCTION_SIZE;
138     pxTopOfStack--;
139 
140     *pxTopOfStack = ( StackType_t ) 0xaaaaaaaa;    /* R14 */
141     pxTopOfStack--;
142     *pxTopOfStack = ( StackType_t ) pxOriginalTOS; /* Stack used when task starts goes in R13. */
143     pxTopOfStack--;
144     *pxTopOfStack = ( StackType_t ) 0x12121212;    /* R12 */
145     pxTopOfStack--;
146     *pxTopOfStack = ( StackType_t ) 0x11111111;    /* R11 */
147     pxTopOfStack--;
148     *pxTopOfStack = ( StackType_t ) 0x10101010;    /* R10 */
149     pxTopOfStack--;
150     *pxTopOfStack = ( StackType_t ) 0x09090909;    /* R9 */
151     pxTopOfStack--;
152     *pxTopOfStack = ( StackType_t ) 0x08080808;    /* R8 */
153     pxTopOfStack--;
154     *pxTopOfStack = ( StackType_t ) 0x07070707;    /* R7 */
155     pxTopOfStack--;
156     *pxTopOfStack = ( StackType_t ) 0x06060606;    /* R6 */
157     pxTopOfStack--;
158     *pxTopOfStack = ( StackType_t ) 0x05050505;    /* R5 */
159     pxTopOfStack--;
160     *pxTopOfStack = ( StackType_t ) 0x04040404;    /* R4 */
161     pxTopOfStack--;
162     *pxTopOfStack = ( StackType_t ) 0x03030303;    /* R3 */
163     pxTopOfStack--;
164     *pxTopOfStack = ( StackType_t ) 0x02020202;    /* R2 */
165     pxTopOfStack--;
166     *pxTopOfStack = ( StackType_t ) 0x01010101;    /* R1 */
167     pxTopOfStack--;
168 
169     /* When the task starts is will expect to find the function parameter in
170      * R0. */
171     *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
172     pxTopOfStack--;
173 
174     /* The status register is set for system mode, with interrupts enabled. */
175     *pxTopOfStack = ( StackType_t ) portINITIAL_SPSR;
176     pxTopOfStack--;
177 
178     /* Interrupt flags cannot always be stored on the stack and will
179      * instead be stored in a variable, which is then saved as part of the
180      * tasks context. */
181     *pxTopOfStack = portNO_CRITICAL_NESTING;
182 
183     return pxTopOfStack;
184 }
185 /*-----------------------------------------------------------*/
186 
xPortStartScheduler(void)187 BaseType_t xPortStartScheduler( void )
188 {
189     extern void vPortStartFirstTask( void );
190 
191     /* Start the timer that generates the tick ISR.  Interrupts are disabled
192      * here already. */
193     prvSetupTimerInterrupt();
194 
195     /* Start the first task. */
196     vPortStartFirstTask();
197 
198     /* Should not get here! */
199     return 0;
200 }
201 /*-----------------------------------------------------------*/
202 
vPortEndScheduler(void)203 void vPortEndScheduler( void )
204 {
205     /* It is unlikely that the ARM port will require this function as there
206      * is nothing to return to.  */
207 }
208 /*-----------------------------------------------------------*/
209 
210 /* This function is called from an asm wrapper, so does not require the __irq
211  * keyword. */
212 #if configUSE_WATCHDOG_TICK == 1
213 
prvFindFactors(u32 n,u16 * a,u32 * b)214     static void prvFindFactors( u32 n,
215                                 u16 * a,
216                                 u32 * b )
217     {
218         /* This function is copied from the ST STR7 library and is
219          * copyright STMicroelectronics.  Reproduced with permission. */
220 
221         u32 b0;
222         u16 a0;
223         int32_t err, err_min = n;
224 
225         *a = a0 = ( ( n - 1 ) / 65536ul ) + 1;
226         *b = b0 = n / *a;
227 
228         for( ; *a <= 256; ( *a )++ )
229         {
230             *b = n / *a;
231             err = ( int32_t ) *a * ( int32_t ) *b - ( int32_t ) n;
232 
233             if( abs( err ) > ( *a / 2 ) )
234             {
235                 ( *b )++;
236                 err = ( int32_t ) *a * ( int32_t ) *b - ( int32_t ) n;
237             }
238 
239             if( abs( err ) < abs( err_min ) )
240             {
241                 err_min = err;
242                 a0 = *a;
243                 b0 = *b;
244 
245                 if( err == 0 )
246                 {
247                     break;
248                 }
249             }
250         }
251 
252         *a = a0;
253         *b = b0;
254     }
255     /*-----------------------------------------------------------*/
256 
prvSetupTimerInterrupt(void)257     static void prvSetupTimerInterrupt( void )
258     {
259         WDG_InitTypeDef xWdg;
260         uint16_t a;
261         uint32_t n = configCPU_PERIPH_HZ / configTICK_RATE_HZ, b;
262 
263         /* Configure the watchdog as a free running timer that generates a
264          * periodic interrupt. */
265 
266         SCU_APBPeriphClockConfig( __WDG, ENABLE );
267         WDG_DeInit();
268         WDG_StructInit( &xWdg );
269         prvFindFactors( n, &a, &b );
270         xWdg.WDG_Prescaler = a - 1;
271         xWdg.WDG_Preload = b - 1;
272         WDG_Init( &xWdg );
273         WDG_ITConfig( ENABLE );
274 
275         /* Configure the VIC for the WDG interrupt. */
276         VIC_Config( WDG_ITLine, VIC_IRQ, 10 );
277         VIC_ITCmd( WDG_ITLine, ENABLE );
278 
279         /* Install the default handlers for both VIC's. */
280         VIC0->DVAR = ( uint32_t ) prvDefaultHandler;
281         VIC1->DVAR = ( uint32_t ) prvDefaultHandler;
282 
283         WDG_Cmd( ENABLE );
284     }
285     /*-----------------------------------------------------------*/
286 
WDG_IRQHandler(void)287     void WDG_IRQHandler( void )
288     {
289         {
290             /* Increment the tick counter. */
291             if( xTaskIncrementTick() != pdFALSE )
292             {
293                 /* Select a new task to execute. */
294                 vTaskSwitchContext();
295             }
296 
297             /* Clear the interrupt in the watchdog. */
298             WDG->SR &= ~0x0001;
299         }
300     }
301 
302 #else /* if configUSE_WATCHDOG_TICK == 1 */
303 
prvFindFactors(u32 n,u8 * a,u16 * b)304     static void prvFindFactors( u32 n,
305                                 u8 * a,
306                                 u16 * b )
307     {
308         /* This function is copied from the ST STR7 library and is
309          * copyright STMicroelectronics.  Reproduced with permission. */
310 
311         u16 b0;
312         u8 a0;
313         int32_t err, err_min = n;
314 
315 
316         *a = a0 = ( ( n - 1 ) / 256 ) + 1;
317         *b = b0 = n / *a;
318 
319         for( ; *a <= 256; ( *a )++ )
320         {
321             *b = n / *a;
322             err = ( int32_t ) *a * ( int32_t ) *b - ( int32_t ) n;
323 
324             if( abs( err ) > ( *a / 2 ) )
325             {
326                 ( *b )++;
327                 err = ( int32_t ) *a * ( int32_t ) *b - ( int32_t ) n;
328             }
329 
330             if( abs( err ) < abs( err_min ) )
331             {
332                 err_min = err;
333                 a0 = *a;
334                 b0 = *b;
335 
336                 if( err == 0 )
337                 {
338                     break;
339                 }
340             }
341         }
342 
343         *a = a0;
344         *b = b0;
345     }
346     /*-----------------------------------------------------------*/
347 
prvSetupTimerInterrupt(void)348     static void prvSetupTimerInterrupt( void )
349     {
350         uint8_t a;
351         uint16_t b;
352         uint32_t n = configCPU_PERIPH_HZ / configTICK_RATE_HZ;
353 
354         TIM_InitTypeDef timer;
355 
356         SCU_APBPeriphClockConfig( __TIM23, ENABLE );
357         TIM_DeInit( TIM2 );
358         TIM_StructInit( &timer );
359         prvFindFactors( n, &a, &b );
360 
361         timer.TIM_Mode = TIM_OCM_CHANNEL_1;
362         timer.TIM_OC1_Modes = TIM_TIMING;
363         timer.TIM_Clock_Source = TIM_CLK_APB;
364         timer.TIM_Clock_Edge = TIM_CLK_EDGE_RISING;
365         timer.TIM_Prescaler = a - 1;
366         timer.TIM_Pulse_Level_1 = TIM_HIGH;
367         timer.TIM_Pulse_Length_1 = s_nPulseLength = b - 1;
368 
369         TIM_Init( TIM2, &timer );
370         TIM_ITConfig( TIM2, TIM_IT_OC1, ENABLE );
371         /* Configure the VIC for the WDG interrupt. */
372         VIC_Config( TIM2_ITLine, VIC_IRQ, 10 );
373         VIC_ITCmd( TIM2_ITLine, ENABLE );
374 
375         /* Install the default handlers for both VIC's. */
376         VIC0->DVAR = ( uint32_t ) prvDefaultHandler;
377         VIC1->DVAR = ( uint32_t ) prvDefaultHandler;
378 
379         TIM_CounterCmd( TIM2, TIM_CLEAR );
380         TIM_CounterCmd( TIM2, TIM_START );
381     }
382     /*-----------------------------------------------------------*/
383 
TIM2_IRQHandler(void)384     void TIM2_IRQHandler( void )
385     {
386         /* Reset the timer counter to avoid overflow. */
387         TIM2->OC1R += s_nPulseLength;
388 
389         /* Increment the tick counter. */
390         if( xTaskIncrementTick() != pdFALSE )
391         {
392             /* Select a new task to run. */
393             vTaskSwitchContext();
394         }
395 
396         /* Clear the interrupt in the watchdog. */
397         TIM2->SR &= ~TIM_FLAG_OC1;
398     }
399 
400 #endif /* USE_WATCHDOG_TICK */
401 
402 /*-----------------------------------------------------------*/
403 
vPortEnterCritical(void)404 __arm __interwork void vPortEnterCritical( void )
405 {
406     /* Disable interrupts first! */
407     portDISABLE_INTERRUPTS();
408 
409     /* Now that interrupts are disabled, ulCriticalNesting can be accessed
410      * directly.  Increment ulCriticalNesting to keep a count of how many times
411      * portENTER_CRITICAL() has been called. */
412     ulCriticalNesting++;
413 }
414 /*-----------------------------------------------------------*/
415 
vPortExitCritical(void)416 __arm __interwork void vPortExitCritical( void )
417 {
418     if( ulCriticalNesting > portNO_CRITICAL_NESTING )
419     {
420         /* Decrement the nesting count as we are leaving a critical section. */
421         ulCriticalNesting--;
422 
423         /* If the nesting level has reached zero then interrupts should be
424          * re-enabled. */
425         if( ulCriticalNesting == portNO_CRITICAL_NESTING )
426         {
427             portENABLE_INTERRUPTS();
428         }
429     }
430 }
431 /*-----------------------------------------------------------*/
432 
prvDefaultHandler(void)433 static void prvDefaultHandler( void )
434 {
435 }
436