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 RXv3 DPFPU port.
31 *----------------------------------------------------------*/
32
33 #warning Testing for DFPU support in this port is not yet complete
34
35 /* Scheduler includes. */
36 #include "FreeRTOS.h"
37 #include "task.h"
38
39 /* Library includes. */
40 #include "string.h"
41
42 /* Hardware specifics. */
43 #if ( configINCLUDE_PLATFORM_H_INSTEAD_OF_IODEFINE_H == 1 )
44
45 #include "platform.h"
46
47 #else /* configINCLUDE_PLATFORM_H_INSTEAD_OF_IODEFINE_H */
48
49 #include "iodefine.h"
50
51 #endif /* configINCLUDE_PLATFORM_H_INSTEAD_OF_IODEFINE_H */
52
53 /*-----------------------------------------------------------*/
54
55 /* Tasks should start with interrupts enabled and in Supervisor mode, therefore
56 * PSW is set with U and I set, and PM and IPL clear. */
57 #define portINITIAL_PSW ( ( StackType_t ) 0x00030000 )
58 #define portINITIAL_FPSW ( ( StackType_t ) 0x00000100 )
59 #define portINITIAL_DPSW ( ( StackType_t ) 0x00000100 )
60 #define portINITIAL_DCMR ( ( StackType_t ) 0x00000000 )
61 #define portINITIAL_DECNT ( ( StackType_t ) 0x00000001 )
62
63 /* Tasks are not created with a DPFPU context, but can be given a DPFPU context
64 * after they have been created. A variable is stored as part of the tasks context
65 * that holds portNO_DPFPU_CONTEXT if the task does not have a DPFPU context, or
66 * any other value if the task does have a DPFPU context. */
67 #define portNO_DPFPU_CONTEXT ( ( StackType_t ) 0 )
68 #define portHAS_DPFPU_CONTEXT ( ( StackType_t ) 1 )
69
70 /* The space on the stack required to hold the DPFPU data registers. This is 16
71 * 64-bit registers. */
72 #define portDPFPU_DATA_REGISTER_WORDS ( 16 * 2 )
73
74 /*-----------------------------------------------------------*/
75
76 /* The following lines are to ensure vSoftwareInterruptEntry can be referenced,
77 * and therefore installed in the vector table, when the FreeRTOS code is built
78 * as a library. */
79 extern BaseType_t vSoftwareInterruptEntry;
80 const BaseType_t * p_vSoftwareInterruptEntry = &vSoftwareInterruptEntry;
81
82 /*-----------------------------------------------------------*/
83
84 /*
85 * Function to start the first task executing - written in asm code as direct
86 * access to registers is required.
87 */
88 static void prvStartFirstTask( void );
89
90 /*
91 * Software interrupt handler. Performs the actual context switch (saving and
92 * restoring of registers). Written in asm code as direct register access is
93 * required.
94 */
95 static void prvYieldHandler( void );
96
97 /*
98 * The entry point for the software interrupt handler. This is the function
99 * that calls the inline asm function prvYieldHandler(). It is installed in
100 * the vector table, but the code that installs it is in prvYieldHandler rather
101 * than using a #pragma.
102 */
103 void vSoftwareInterruptISR( void );
104
105 /*
106 * The tick ISR handler. The peripheral used is configured by the application
107 * via a hook/callback function.
108 */
109 void vTickISR( void );
110
111 /*-----------------------------------------------------------*/
112
113 /* Saved as part of the task context. If ulPortTaskHasDPFPUContext is non-zero
114 * then a DPFPU context must be saved and restored for the task. */
115 #if ( configUSE_TASK_DPFPU_SUPPORT == 1 )
116
117 StackType_t ulPortTaskHasDPFPUContext = portNO_DPFPU_CONTEXT;
118
119 #endif /* configUSE_TASK_DPFPU_SUPPORT */
120
121 /* This is accessed by the inline assembler functions so is file scope for
122 * convenience. */
123 extern void * pxCurrentTCB;
124 extern void vTaskSwitchContext( void );
125
126 /*-----------------------------------------------------------*/
127
128 /*
129 * See header file for description.
130 */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)131 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
132 TaskFunction_t pxCode,
133 void * pvParameters )
134 {
135 /* R0 is not included as it is the stack pointer. */
136
137 *pxTopOfStack = 0x00;
138 pxTopOfStack--;
139 *pxTopOfStack = portINITIAL_PSW;
140 pxTopOfStack--;
141 *pxTopOfStack = ( StackType_t ) pxCode;
142
143 /* When debugging it can be useful if every register is set to a known
144 * value. Otherwise code space can be saved by just setting the registers
145 * that need to be set. */
146 #ifdef USE_FULL_REGISTER_INITIALISATION
147 {
148 pxTopOfStack--;
149 *pxTopOfStack = 0xffffffff; /* r15. */
150 pxTopOfStack--;
151 *pxTopOfStack = 0xeeeeeeee;
152 pxTopOfStack--;
153 *pxTopOfStack = 0xdddddddd;
154 pxTopOfStack--;
155 *pxTopOfStack = 0xcccccccc;
156 pxTopOfStack--;
157 *pxTopOfStack = 0xbbbbbbbb;
158 pxTopOfStack--;
159 *pxTopOfStack = 0xaaaaaaaa;
160 pxTopOfStack--;
161 *pxTopOfStack = 0x99999999;
162 pxTopOfStack--;
163 *pxTopOfStack = 0x88888888;
164 pxTopOfStack--;
165 *pxTopOfStack = 0x77777777;
166 pxTopOfStack--;
167 *pxTopOfStack = 0x66666666;
168 pxTopOfStack--;
169 *pxTopOfStack = 0x55555555;
170 pxTopOfStack--;
171 *pxTopOfStack = 0x44444444;
172 pxTopOfStack--;
173 *pxTopOfStack = 0x33333333;
174 pxTopOfStack--;
175 *pxTopOfStack = 0x22222222;
176 pxTopOfStack--;
177 }
178 #else /* ifdef USE_FULL_REGISTER_INITIALISATION */
179 {
180 pxTopOfStack -= 15;
181 }
182 #endif /* ifdef USE_FULL_REGISTER_INITIALISATION */
183
184 *pxTopOfStack = ( StackType_t ) pvParameters; /* R1 */
185 pxTopOfStack--;
186 *pxTopOfStack = portINITIAL_FPSW;
187 pxTopOfStack--;
188 *pxTopOfStack = 0x11111111; /* Accumulator 1. */
189 pxTopOfStack--;
190 *pxTopOfStack = 0x22222222; /* Accumulator 1. */
191 pxTopOfStack--;
192 *pxTopOfStack = 0x33333333; /* Accumulator 1. */
193 pxTopOfStack--;
194 *pxTopOfStack = 0x44444444; /* Accumulator 0. */
195 pxTopOfStack--;
196 *pxTopOfStack = 0x55555555; /* Accumulator 0. */
197 pxTopOfStack--;
198 *pxTopOfStack = 0x66666666; /* Accumulator 0. */
199
200 #if ( configUSE_TASK_DPFPU_SUPPORT == 1 )
201 {
202 /* The task will start without a DPFPU context. A task that
203 * uses the DPFPU hardware must call vPortTaskUsesDPFPU() before
204 * executing any floating point instructions. */
205 pxTopOfStack--;
206 *pxTopOfStack = portNO_DPFPU_CONTEXT;
207 }
208 #elif ( configUSE_TASK_DPFPU_SUPPORT == 2 )
209 {
210 /* The task will start with a DPFPU context. Leave enough
211 * space for the registers - and ensure they are initialised if desired. */
212 #ifdef USE_FULL_REGISTER_INITIALISATION
213 {
214 pxTopOfStack -= 2;
215 *(double *)pxTopOfStack = 1515.1515; /* DR15. */
216 pxTopOfStack -= 2;
217 *(double *)pxTopOfStack = 1414.1414; /* DR14. */
218 pxTopOfStack -= 2;
219 *(double *)pxTopOfStack = 1313.1313; /* DR13. */
220 pxTopOfStack -= 2;
221 *(double *)pxTopOfStack = 1212.1212; /* DR12. */
222 pxTopOfStack -= 2;
223 *(double *)pxTopOfStack = 1111.1111; /* DR11. */
224 pxTopOfStack -= 2;
225 *(double *)pxTopOfStack = 1010.1010; /* DR10. */
226 pxTopOfStack -= 2;
227 *(double *)pxTopOfStack = 909.0909; /* DR9. */
228 pxTopOfStack -= 2;
229 *(double *)pxTopOfStack = 808.0808; /* DR8. */
230 pxTopOfStack -= 2;
231 *(double *)pxTopOfStack = 707.0707; /* DR7. */
232 pxTopOfStack -= 2;
233 *(double *)pxTopOfStack = 606.0606; /* DR6. */
234 pxTopOfStack -= 2;
235 *(double *)pxTopOfStack = 505.0505; /* DR5. */
236 pxTopOfStack -= 2;
237 *(double *)pxTopOfStack = 404.0404; /* DR4. */
238 pxTopOfStack -= 2;
239 *(double *)pxTopOfStack = 303.0303; /* DR3. */
240 pxTopOfStack -= 2;
241 *(double *)pxTopOfStack = 202.0202; /* DR2. */
242 pxTopOfStack -= 2;
243 *(double *)pxTopOfStack = 101.0101; /* DR1. */
244 pxTopOfStack -= 2;
245 *(double *)pxTopOfStack = 9876.54321;/* DR0. */
246 }
247 #else /* ifdef USE_FULL_REGISTER_INITIALISATION */
248 {
249 pxTopOfStack -= portDPFPU_DATA_REGISTER_WORDS;
250 memset( pxTopOfStack, 0x00, portDPFPU_DATA_REGISTER_WORDS * sizeof( StackType_t ) );
251 }
252 #endif /* ifdef USE_FULL_REGISTER_INITIALISATION */
253 pxTopOfStack--;
254 *pxTopOfStack = portINITIAL_DECNT; /* DECNT. */
255 pxTopOfStack--;
256 *pxTopOfStack = portINITIAL_DCMR; /* DCMR. */
257 pxTopOfStack--;
258 *pxTopOfStack = portINITIAL_DPSW; /* DPSW. */
259 }
260 #elif ( configUSE_TASK_DPFPU_SUPPORT == 0 )
261 {
262 /* Omit DPFPU support. */
263 }
264 #else /* if ( configUSE_TASK_DPFPU_SUPPORT == 1 ) */
265 {
266 #error Invalid configUSE_TASK_DPFPU_SUPPORT setting - configUSE_TASK_DPFPU_SUPPORT must be set to 0, 1, 2, or left undefined.
267 }
268 #endif /* if ( configUSE_TASK_DPFPU_SUPPORT == 1 ) */
269
270 return pxTopOfStack;
271 }
272 /*-----------------------------------------------------------*/
273
274 #if ( configUSE_TASK_DPFPU_SUPPORT == 1 )
275
vPortTaskUsesDPFPU(void)276 void vPortTaskUsesDPFPU( void )
277 {
278 /* A task is registering the fact that it needs a DPFPU context. Set the
279 * DPFPU flag (which is saved as part of the task context). */
280 ulPortTaskHasDPFPUContext = portHAS_DPFPU_CONTEXT;
281 }
282
283 #endif /* configUSE_TASK_DPFPU_SUPPORT */
284 /*-----------------------------------------------------------*/
285
xPortStartScheduler(void)286 BaseType_t xPortStartScheduler( void )
287 {
288 extern void vApplicationSetupTimerInterrupt( void );
289
290 /* Use pxCurrentTCB just so it does not get optimised away. */
291 if( pxCurrentTCB != NULL )
292 {
293 /* Call an application function to set up the timer that will generate the
294 * tick interrupt. This way the application can decide which peripheral to
295 * use. A demo application is provided to show a suitable example. */
296 vApplicationSetupTimerInterrupt();
297
298 /* Enable the software interrupt. */
299 _IEN( _ICU_SWINT ) = 1;
300
301 /* Ensure the software interrupt is clear. */
302 _IR( _ICU_SWINT ) = 0;
303
304 /* Ensure the software interrupt is set to the kernel priority. */
305 _IPR( _ICU_SWINT ) = configKERNEL_INTERRUPT_PRIORITY;
306
307 /* Start the first task. */
308 prvStartFirstTask();
309 }
310
311 /* Just to make sure the function is not optimised away. */
312 ( void ) vSoftwareInterruptISR();
313
314 /* Should not get here. */
315 return pdFAIL;
316 }
317 /*-----------------------------------------------------------*/
318
vPortEndScheduler(void)319 void vPortEndScheduler( void )
320 {
321 /* Not implemented in ports where there is nothing to return to.
322 * Artificially force an assert. */
323 configASSERT( pxCurrentTCB == NULL );
324
325 /* The following line is just to prevent the symbol getting optimised away. */
326 ( void ) vTaskSwitchContext();
327 }
328 /*-----------------------------------------------------------*/
329
330 #pragma inline_asm prvStartFirstTask
prvStartFirstTask(void)331 static void prvStartFirstTask( void )
332 {
333 #ifndef __CDT_PARSER__
334
335 /* When starting the scheduler there is nothing that needs moving to the
336 * interrupt stack because the function is not called from an interrupt.
337 * Just ensure the current stack is the user stack. */
338 SETPSW U
339
340
341 /* Obtain the location of the stack associated with which ever task
342 * pxCurrentTCB is currently pointing to. */
343 MOV.L # _pxCurrentTCB, R15
344 MOV.L [ R15 ], R15
345 MOV.L [ R15 ], R0
346
347
348 /* Restore the registers from the stack of the task pointed to by
349 * pxCurrentTCB. */
350
351 #if ( configUSE_TASK_DPFPU_SUPPORT == 1 )
352
353 /* The restored ulPortTaskHasDPFPUContext is to be zero here.
354 * So, it is never necessary to restore the DPFPU context here. */
355 POP R15
356 MOV.L # _ulPortTaskHasDPFPUContext, R14
357 MOV.L R15, [ R14 ]
358
359 #elif ( configUSE_TASK_DPFPU_SUPPORT == 2 )
360
361 /* Restore the DPFPU context. */
362 DPOPM.L DPSW-DECNT
363 DPOPM.D DR0-DR15
364
365 #endif /* if ( configUSE_TASK_DPFPU_SUPPORT == 1 ) */
366
367 POP R15
368
369 /* Accumulator low 32 bits. */
370 MVTACLO R15, A0
371 POP R15
372
373 /* Accumulator high 32 bits. */
374 MVTACHI R15, A0
375 POP R15
376
377 /* Accumulator guard. */
378 MVTACGU R15, A0
379 POP R15
380
381 /* Accumulator low 32 bits. */
382 MVTACLO R15, A1
383 POP R15
384
385 /* Accumulator high 32 bits. */
386 MVTACHI R15, A1
387 POP R15
388
389 /* Accumulator guard. */
390 MVTACGU R15, A1
391 POP R15
392
393 /* Floating point status word. */
394 MVTC R15, FPSW
395
396 /* R1 to R15 - R0 is not included as it is the SP. */
397 POPM R1-R15
398
399 /* This pops the remaining registers. */
400 RTE
401 NOP
402 NOP
403
404 #endif /* ifndef __CDT_PARSER__ */
405 }
406 /*-----------------------------------------------------------*/
407
vSoftwareInterruptISR(void)408 void vSoftwareInterruptISR( void )
409 {
410 prvYieldHandler();
411 }
412 /*-----------------------------------------------------------*/
413
414 #pragma inline_asm prvYieldHandler
prvYieldHandler(void)415 static void prvYieldHandler( void )
416 {
417 #ifndef __CDT_PARSER__
418
419 /* Re-enable interrupts. */
420 SETPSW I
421
422
423 /* Move the data that was automatically pushed onto the interrupt stack when
424 * the interrupt occurred from the interrupt stack to the user stack.
425 *
426 * R15 is saved before it is clobbered. */
427 PUSH.L R15
428
429 /* Read the user stack pointer. */
430 MVFC USP, R15
431
432 /* Move the address down to the data being moved. */
433 SUB # 12, R15
434 MVTC R15, USP
435
436 /* Copy the data across, R15, then PC, then PSW. */
437 MOV.L [ R0 ], [ R15 ]
438 MOV.L 4[ R0 ], 4[ R15 ]
439 MOV.L 8[ R0 ], 8[ R15 ]
440
441 /* Move the interrupt stack pointer to its new correct position. */
442 ADD # 12, R0
443
444 /* All the rest of the registers are saved directly to the user stack. */
445 SETPSW U
446
447 /* Save the rest of the general registers (R15 has been saved already). */
448 PUSHM R1-R14
449
450 /* Save the FPSW and accumulators. */
451 MVFC FPSW, R15
452 PUSH.L R15
453 MVFACGU # 0, A1, R15
454 PUSH.L R15
455 MVFACHI # 0, A1, R15
456 PUSH.L R15
457 MVFACLO # 0, A1, R15 /* Low order word. */
458 PUSH.L R15
459 MVFACGU # 0, A0, R15
460 PUSH.L R15
461 MVFACHI # 0, A0, R15
462 PUSH.L R15
463 MVFACLO # 0, A0, R15 /* Low order word. */
464 PUSH.L R15
465
466 #if ( configUSE_TASK_DPFPU_SUPPORT == 1 )
467
468 /* Does the task have a DPFPU context that needs saving? If
469 * ulPortTaskHasDPFPUContext is 0 then no. */
470 MOV.L # _ulPortTaskHasDPFPUContext, R15
471 MOV.L [ R15 ], R15
472 CMP # 0, R15
473
474 /* Save the DPFPU context, if any. */
475 BEQ.B ?+
476 DPUSHM.D DR0-DR15
477 DPUSHM.L DPSW-DECNT
478 ?:
479
480 /* Save ulPortTaskHasDPFPUContext itself. */
481 PUSH.L R15
482
483 #elif ( configUSE_TASK_DPFPU_SUPPORT == 2 )
484
485 /* Save the DPFPU context, always. */
486 DPUSHM.D DR0-DR15
487 DPUSHM.L DPSW-DECNT
488
489 #endif /* if ( configUSE_TASK_DPFPU_SUPPORT == 1 ) */
490
491
492 /* Save the stack pointer to the TCB. */
493 MOV.L # _pxCurrentTCB, R15
494 MOV.L [ R15 ], R15
495 MOV.L R0, [ R15 ]
496
497
498 /* Ensure the interrupt mask is set to the syscall priority while the kernel
499 * structures are being accessed. */
500 MVTIPL # configMAX_SYSCALL_INTERRUPT_PRIORITY
501
502 /* Select the next task to run. */
503 BSR.A _vTaskSwitchContext
504
505 /* Reset the interrupt mask as no more data structure access is required. */
506 MVTIPL # configKERNEL_INTERRUPT_PRIORITY
507
508
509 /* Load the stack pointer of the task that is now selected as the Running
510 * state task from its TCB. */
511 MOV.L # _pxCurrentTCB, R15
512 MOV.L [ R15 ], R15
513 MOV.L [ R15 ], R0
514
515
516 /* Restore the context of the new task. The PSW (Program Status Word) and
517 * PC will be popped by the RTE instruction. */
518
519 #if ( configUSE_TASK_DPFPU_SUPPORT == 1 )
520
521 /* Is there a DPFPU context to restore? If the restored
522 * ulPortTaskHasDPFPUContext is zero then no. */
523 POP R15
524 MOV.L # _ulPortTaskHasDPFPUContext, R14
525 MOV.L R15, [ R14 ]
526 CMP # 0, R15
527
528 /* Restore the DPFPU context, if any. */
529 BEQ.B ?+
530 DPOPM.L DPSW-DECNT
531 DPOPM.D DR0-DR15
532 ?:
533
534 #elif ( configUSE_TASK_DPFPU_SUPPORT == 2 )
535
536 /* Restore the DPFPU context, always. */
537 DPOPM.L DPSW-DECNT
538 DPOPM.D DR0-DR15
539
540 #endif /* if ( configUSE_TASK_DPFPU_SUPPORT == 1 ) */
541
542 POP R15
543
544 /* Accumulator low 32 bits. */
545 MVTACLO R15, A0
546 POP R15
547
548 /* Accumulator high 32 bits. */
549 MVTACHI R15, A0
550 POP R15
551
552 /* Accumulator guard. */
553 MVTACGU R15, A0
554 POP R15
555
556 /* Accumulator low 32 bits. */
557 MVTACLO R15, A1
558 POP R15
559
560 /* Accumulator high 32 bits. */
561 MVTACHI R15, A1
562 POP R15
563
564 /* Accumulator guard. */
565 MVTACGU R15, A1
566 POP R15
567 MVTC R15, FPSW
568 POPM R1-R15
569 RTE
570 NOP
571 NOP
572
573 #endif /* ifndef __CDT_PARSER__ */
574 }
575 /*-----------------------------------------------------------*/
576
577 #pragma interrupt ( vTickISR( vect = _VECT( configTICK_VECTOR ), enable ) )
vTickISR(void)578 void vTickISR( void )
579 {
580 /* Increment the tick, and perform any processing the new tick value
581 * necessitates. Ensure IPL is at the max syscall value first. */
582 set_ipl( configMAX_SYSCALL_INTERRUPT_PRIORITY );
583 {
584 if( xTaskIncrementTick() != pdFALSE )
585 {
586 taskYIELD();
587 }
588 }
589 set_ipl( configKERNEL_INTERRUPT_PRIORITY );
590 }
591 /*-----------------------------------------------------------*/
592