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