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