xref: /Kernel-v10.6.2/portable/Renesas/RX700v3_DPFPU/port.c (revision ef7b253b56c9788077f5ecd6c9deb4021923d646)
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