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