xref: /Kernel-v10.6.2/portable/IAR/STR91x/port.c (revision ef7b253b56c9788077f5ecd6c9deb4021923d646)
1 /*
2  * FreeRTOS Kernel V10.6.2
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, TaskFunction_t pxCode, void *pvParameters )
120 {
121     StackType_t *pxOriginalTOS;
122 
123     pxOriginalTOS = pxTopOfStack;
124 
125     /* To ensure asserts in tasks.c don't fail, although in this case the assert
126     is not really required. */
127     pxTopOfStack--;
128 
129     /* Setup the initial stack of the task.  The stack is set exactly as
130     expected by the portRESTORE_CONTEXT() macro. */
131 
132     /* First on the stack is the return address - which in this case is the
133     start of the task.  The offset is added to make the return address appear
134     as it would within an IRQ ISR. */
135     *pxTopOfStack = ( StackType_t ) pxCode + portINSTRUCTION_SIZE;
136     pxTopOfStack--;
137 
138     *pxTopOfStack = ( StackType_t ) 0xaaaaaaaa; /* R14 */
139     pxTopOfStack--;
140     *pxTopOfStack = ( StackType_t ) pxOriginalTOS; /* Stack used when task starts goes in R13. */
141     pxTopOfStack--;
142     *pxTopOfStack = ( StackType_t ) 0x12121212; /* R12 */
143     pxTopOfStack--;
144     *pxTopOfStack = ( StackType_t ) 0x11111111; /* R11 */
145     pxTopOfStack--;
146     *pxTopOfStack = ( StackType_t ) 0x10101010; /* R10 */
147     pxTopOfStack--;
148     *pxTopOfStack = ( StackType_t ) 0x09090909; /* R9 */
149     pxTopOfStack--;
150     *pxTopOfStack = ( StackType_t ) 0x08080808; /* R8 */
151     pxTopOfStack--;
152     *pxTopOfStack = ( StackType_t ) 0x07070707; /* R7 */
153     pxTopOfStack--;
154     *pxTopOfStack = ( StackType_t ) 0x06060606; /* R6 */
155     pxTopOfStack--;
156     *pxTopOfStack = ( StackType_t ) 0x05050505; /* R5 */
157     pxTopOfStack--;
158     *pxTopOfStack = ( StackType_t ) 0x04040404; /* R4 */
159     pxTopOfStack--;
160     *pxTopOfStack = ( StackType_t ) 0x03030303; /* R3 */
161     pxTopOfStack--;
162     *pxTopOfStack = ( StackType_t ) 0x02020202; /* R2 */
163     pxTopOfStack--;
164     *pxTopOfStack = ( StackType_t ) 0x01010101; /* R1 */
165     pxTopOfStack--;
166 
167     /* When the task starts is will expect to find the function parameter in
168     R0. */
169     *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */
170     pxTopOfStack--;
171 
172     /* The status register is set for system mode, with interrupts enabled. */
173     *pxTopOfStack = ( StackType_t ) portINITIAL_SPSR;
174     pxTopOfStack--;
175 
176     /* Interrupt flags cannot always be stored on the stack and will
177     instead be stored in a variable, which is then saved as part of the
178     tasks context. */
179     *pxTopOfStack = portNO_CRITICAL_NESTING;
180 
181     return pxTopOfStack;
182 }
183 /*-----------------------------------------------------------*/
184 
xPortStartScheduler(void)185 BaseType_t xPortStartScheduler( void )
186 {
187 extern void vPortStartFirstTask( void );
188 
189     /* Start the timer that generates the tick ISR.  Interrupts are disabled
190     here already. */
191     prvSetupTimerInterrupt();
192 
193     /* Start the first task. */
194     vPortStartFirstTask();
195 
196     /* Should not get here! */
197     return 0;
198 }
199 /*-----------------------------------------------------------*/
200 
vPortEndScheduler(void)201 void vPortEndScheduler( void )
202 {
203     /* It is unlikely that the ARM port will require this function as there
204     is nothing to return to.  */
205 }
206 /*-----------------------------------------------------------*/
207 
208 /* This function is called from an asm wrapper, so does not require the __irq
209 keyword. */
210 #if configUSE_WATCHDOG_TICK == 1
211 
prvFindFactors(u32 n,u16 * a,u32 * b)212     static void prvFindFactors(u32 n, u16 *a, u32 *b)
213     {
214         /* This function is copied from the ST STR7 library and is
215         copyright STMicroelectronics.  Reproduced with permission. */
216 
217         u32 b0;
218         u16 a0;
219         int32_t err, err_min=n;
220 
221         *a = a0 = ((n-1)/65536ul) + 1;
222         *b = b0 = n / *a;
223 
224         for (; *a <= 256; (*a)++)
225         {
226             *b = n / *a;
227             err = (int32_t)*a * (int32_t)*b - (int32_t)n;
228             if (abs(err) > (*a / 2))
229             {
230                 (*b)++;
231                 err = (int32_t)*a * (int32_t)*b - (int32_t)n;
232             }
233             if (abs(err) < abs(err_min))
234             {
235                 err_min = err;
236                 a0 = *a;
237                 b0 = *b;
238                 if (err == 0) break;
239             }
240         }
241 
242         *a = a0;
243         *b = b0;
244     }
245     /*-----------------------------------------------------------*/
246 
prvSetupTimerInterrupt(void)247     static void prvSetupTimerInterrupt( void )
248     {
249     WDG_InitTypeDef xWdg;
250     uint16_t a;
251     uint32_t n = configCPU_PERIPH_HZ / configTICK_RATE_HZ, b;
252 
253         /* Configure the watchdog as a free running timer that generates a
254         periodic interrupt. */
255 
256         SCU_APBPeriphClockConfig( __WDG, ENABLE );
257         WDG_DeInit();
258         WDG_StructInit(&xWdg);
259         prvFindFactors( n, &a, &b );
260         xWdg.WDG_Prescaler = a - 1;
261         xWdg.WDG_Preload = b - 1;
262         WDG_Init( &xWdg );
263         WDG_ITConfig(ENABLE);
264 
265         /* Configure the VIC for the WDG interrupt. */
266         VIC_Config( WDG_ITLine, VIC_IRQ, 10 );
267         VIC_ITCmd( WDG_ITLine, ENABLE );
268 
269         /* Install the default handlers for both VIC's. */
270         VIC0->DVAR = ( uint32_t ) prvDefaultHandler;
271         VIC1->DVAR = ( uint32_t ) prvDefaultHandler;
272 
273         WDG_Cmd(ENABLE);
274     }
275     /*-----------------------------------------------------------*/
276 
WDG_IRQHandler(void)277     void WDG_IRQHandler( void )
278     {
279         {
280             /* Increment the tick counter. */
281             if( xTaskIncrementTick() != pdFALSE )
282             {
283                 /* Select a new task to execute. */
284                 vTaskSwitchContext();
285             }
286 
287             /* Clear the interrupt in the watchdog. */
288             WDG->SR &= ~0x0001;
289         }
290     }
291 
292 #else
293 
prvFindFactors(u32 n,u8 * a,u16 * b)294     static void prvFindFactors(u32 n, u8 *a, u16 *b)
295     {
296         /* This function is copied from the ST STR7 library and is
297         copyright STMicroelectronics.  Reproduced with permission. */
298 
299         u16 b0;
300         u8 a0;
301         int32_t err, err_min=n;
302 
303 
304         *a = a0 = ((n-1)/256) + 1;
305         *b = b0 = n / *a;
306 
307         for (; *a <= 256; (*a)++)
308         {
309             *b = n / *a;
310             err = (int32_t)*a * (int32_t)*b - (int32_t)n;
311             if (abs(err) > (*a / 2))
312             {
313                 (*b)++;
314                 err = (int32_t)*a * (int32_t)*b - (int32_t)n;
315             }
316             if (abs(err) < abs(err_min))
317             {
318                 err_min = err;
319                 a0 = *a;
320                 b0 = *b;
321                 if (err == 0) break;
322             }
323         }
324 
325         *a = a0;
326         *b = b0;
327     }
328     /*-----------------------------------------------------------*/
329 
prvSetupTimerInterrupt(void)330     static void prvSetupTimerInterrupt( void )
331     {
332         uint8_t a;
333         uint16_t b;
334         uint32_t n = configCPU_PERIPH_HZ / configTICK_RATE_HZ;
335 
336         TIM_InitTypeDef timer;
337 
338         SCU_APBPeriphClockConfig( __TIM23, ENABLE );
339         TIM_DeInit(TIM2);
340         TIM_StructInit(&timer);
341         prvFindFactors( n, &a, &b );
342 
343         timer.TIM_Mode           = TIM_OCM_CHANNEL_1;
344         timer.TIM_OC1_Modes      = TIM_TIMING;
345         timer.TIM_Clock_Source   = TIM_CLK_APB;
346         timer.TIM_Clock_Edge     = TIM_CLK_EDGE_RISING;
347         timer.TIM_Prescaler      = a-1;
348         timer.TIM_Pulse_Level_1  = TIM_HIGH;
349         timer.TIM_Pulse_Length_1 = s_nPulseLength  = b-1;
350 
351         TIM_Init (TIM2, &timer);
352         TIM_ITConfig(TIM2, TIM_IT_OC1, ENABLE);
353         /* Configure the VIC for the WDG interrupt. */
354         VIC_Config( TIM2_ITLine, VIC_IRQ, 10 );
355         VIC_ITCmd( TIM2_ITLine, ENABLE );
356 
357         /* Install the default handlers for both VIC's. */
358         VIC0->DVAR = ( uint32_t ) prvDefaultHandler;
359         VIC1->DVAR = ( uint32_t ) prvDefaultHandler;
360 
361         TIM_CounterCmd(TIM2, TIM_CLEAR);
362         TIM_CounterCmd(TIM2, TIM_START);
363     }
364     /*-----------------------------------------------------------*/
365 
TIM2_IRQHandler(void)366     void TIM2_IRQHandler( void )
367     {
368         /* Reset the timer counter to avioid overflow. */
369         TIM2->OC1R += s_nPulseLength;
370 
371         /* Increment the tick counter. */
372         if( xTaskIncrementTick() != pdFALSE )
373         {
374             /* Select a new task to run. */
375             vTaskSwitchContext();
376         }
377 
378         /* Clear the interrupt in the watchdog. */
379         TIM2->SR &= ~TIM_FLAG_OC1;
380     }
381 
382 #endif /* USE_WATCHDOG_TICK */
383 
384 /*-----------------------------------------------------------*/
385 
vPortEnterCritical(void)386 __arm __interwork void vPortEnterCritical( void )
387 {
388     /* Disable interrupts first! */
389     portDISABLE_INTERRUPTS();
390 
391     /* Now interrupts are disabled ulCriticalNesting can be accessed
392     directly.  Increment ulCriticalNesting to keep a count of how many times
393     portENTER_CRITICAL() has been called. */
394     ulCriticalNesting++;
395 }
396 /*-----------------------------------------------------------*/
397 
vPortExitCritical(void)398 __arm __interwork void vPortExitCritical( void )
399 {
400     if( ulCriticalNesting > portNO_CRITICAL_NESTING )
401     {
402         /* Decrement the nesting count as we are leaving a critical section. */
403         ulCriticalNesting--;
404 
405         /* If the nesting level has reached zero then interrupts should be
406         re-enabled. */
407         if( ulCriticalNesting == portNO_CRITICAL_NESTING )
408         {
409             portENABLE_INTERRUPTS();
410         }
411     }
412 }
413 /*-----------------------------------------------------------*/
414 
prvDefaultHandler(void)415 static void prvDefaultHandler( void )
416 {
417 }
418