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