xref: /Kernel-v10.6.2/portable/Softune/MB96340/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 #include "FreeRTOS.h"
30 #include "task.h"
31 
32 /*-----------------------------------------------------------
33  * Implementation of functions defined in portable.h for the 16FX port.
34  *----------------------------------------------------------*/
35 
36 /*
37  * Get current value of DPR and ADB registers
38  */
39 StackType_t xGet_DPR_ADB_bank( void );
40 
41 /*
42  * Get current value of DTB and PCB registers
43  */
44 StackType_t xGet_DTB_PCB_bank( void );
45 
46 /*
47  * Sets up the periodic ISR used for the RTOS tick.  This uses RLT0, but
48  * can be done using any given RLT.
49  */
50 static void prvSetupRLT0Interrupt( void );
51 
52 /*-----------------------------------------------------------*/
53 
54 /*
55  * We require the address of the pxCurrentTCB variable, but don't want to know
56  * any details of its type.
57  */
58 typedef void TCB_t;
59 extern volatile TCB_t * volatile pxCurrentTCB;
60 
61 /*-----------------------------------------------------------*/
62 
63 /*
64  * Macro to save a task context to the task stack. This macro  copies the
65  * saved context (AH:AL, DPR:ADB, DTB:PCB , PC and PS) from  the   system
66  * stack to task stack pointed by user stack pointer ( USP  for SMALL and
67  * MEDIUM memory model amd USB:USP for COMPACT  and LARGE memory model ),
68  * then  it pushes the general purpose registers RW0-RW7  on  to the task
69  * stack. Finally the  resultant  stack  pointer  value is saved into the
70  * task  control  block  so  it  can  be retrieved the next time the task
71  * executes.
72  */
73 #if( ( configMEMMODEL == portSMALL ) || ( configMEMMODEL == portMEDIUM ) )
74 
75     #define portSAVE_CONTEXT()                                          \
76             {   __asm(" POPW  A ");                                     \
77                 __asm(" AND  CCR,#H'DF ");                              \
78                 __asm(" PUSHW  A ");                                    \
79                 __asm(" OR   CCR,#H'20 ");                              \
80                 __asm(" POPW  A ");                                     \
81                 __asm(" AND  CCR,#H'DF ");                              \
82                 __asm(" PUSHW  A ");                                    \
83                 __asm(" OR   CCR,#H'20 ");                              \
84                 __asm(" POPW  A ");                                     \
85                 __asm(" AND  CCR,#H'DF ");                              \
86                 __asm(" PUSHW  A ");                                    \
87                 __asm(" OR   CCR,#H'20 ");                              \
88                 __asm(" POPW  A ");                                     \
89                 __asm(" AND  CCR,#H'DF ");                              \
90                 __asm(" PUSHW  A ");                                    \
91                 __asm(" OR   CCR,#H'20 ");                              \
92                 __asm(" POPW  A ");                                     \
93                 __asm(" AND  CCR,#H'DF ");                              \
94                 __asm(" PUSHW  A ");                                    \
95                 __asm(" OR   CCR,#H'20 ");                              \
96                 __asm(" POPW  A ");                                     \
97                 __asm(" AND  CCR,#H'DF ");                              \
98                 __asm(" PUSHW  A ");                                    \
99                 __asm(" PUSHW (RW0,RW1,RW2,RW3,RW4,RW5,RW6,RW7) ");     \
100                 __asm(" MOVW A, _pxCurrentTCB ");                       \
101                 __asm(" MOVW A, SP ");                                  \
102                 __asm(" SWAPW ");                                       \
103                 __asm(" MOVW @AL, AH ");                                \
104                 __asm(" OR   CCR,#H'20 ");                              \
105             }
106 
107 /*
108  * Macro to restore a task context from the task stack.  This is effecti-
109  * vely the reverse of SAVE_CONTEXT(). First the stack pointer  value
110  * (USP for SMALL and MEDIUM memory model amd  USB:USP  for  COMPACT  and
111  * LARGE memory model ) is loaded from the task  control block.  Next the
112  * value of all the general purpose registers RW0-RW7 is retrieved. Fina-
113  * lly it copies of the context ( AH:AL,  DPR:ADB, DTB:PCB, PC and PS) of
114  * the task to be executed upon RETI from user stack to system stack.
115  */
116 
117     #define portRESTORE_CONTEXT()                                       \
118             {   __asm(" MOVW A, _pxCurrentTCB ");                       \
119                 __asm(" MOVW A, @A ");                                  \
120                 __asm(" AND  CCR,#H'DF ");                              \
121                 __asm(" MOVW SP, A ");                                  \
122                 __asm(" POPW (RW0,RW1,RW2,RW3,RW4,RW5,RW6,RW7) ");      \
123                 __asm(" POPW  A ");                                     \
124                 __asm(" OR   CCR,#H'20 ");                              \
125                 __asm(" PUSHW  A ");                                    \
126                 __asm(" AND  CCR,#H'DF ");                              \
127                 __asm(" POPW  A ");                                     \
128                 __asm(" OR   CCR,#H'20 ");                              \
129                 __asm(" PUSHW  A ");                                    \
130                 __asm(" AND  CCR,#H'DF ");                              \
131                 __asm(" POPW  A ");                                     \
132                 __asm(" OR   CCR,#H'20 ");                              \
133                 __asm(" PUSHW  A ");                                    \
134                 __asm(" AND  CCR,#H'DF ");                              \
135                 __asm(" POPW  A ");                                     \
136                 __asm(" OR   CCR,#H'20 ");                              \
137                 __asm(" PUSHW  A ");                                    \
138                 __asm(" AND  CCR,#H'DF ");                              \
139                 __asm(" POPW  A ");                                     \
140                 __asm(" OR   CCR,#H'20 ");                              \
141                 __asm(" PUSHW  A ");                                    \
142                 __asm(" AND  CCR,#H'DF ");                              \
143                 __asm(" POPW  A ");                                     \
144                 __asm(" OR   CCR,#H'20 ");                              \
145                 __asm(" PUSHW  A ");                                    \
146             }
147 
148 #elif( ( configMEMMODEL == portCOMPACT ) || ( configMEMMODEL == portLARGE ) )
149 
150     #define portSAVE_CONTEXT()                                          \
151             {   __asm(" POPW  A ");                                     \
152                 __asm(" AND  CCR,#H'DF ");                              \
153                 __asm(" PUSHW  A ");                                    \
154                 __asm(" OR   CCR,#H'20 ");                              \
155                 __asm(" POPW  A ");                                     \
156                 __asm(" AND  CCR,#H'DF ");                              \
157                 __asm(" PUSHW  A ");                                    \
158                 __asm(" OR   CCR,#H'20 ");                              \
159                 __asm(" POPW  A ");                                     \
160                 __asm(" AND  CCR,#H'DF ");                              \
161                 __asm(" PUSHW  A ");                                    \
162                 __asm(" OR   CCR,#H'20 ");                              \
163                 __asm(" POPW  A ");                                     \
164                 __asm(" AND  CCR,#H'DF ");                              \
165                 __asm(" PUSHW  A ");                                    \
166                 __asm(" OR   CCR,#H'20 ");                              \
167                 __asm(" POPW  A ");                                     \
168                 __asm(" AND  CCR,#H'DF ");                              \
169                 __asm(" PUSHW  A ");                                    \
170                 __asm(" OR   CCR,#H'20 ");                              \
171                 __asm(" POPW  A ");                                     \
172                 __asm(" AND  CCR,#H'DF ");                              \
173                 __asm(" PUSHW  A ");                                    \
174                 __asm(" PUSHW (RW0,RW1,RW2,RW3,RW4,RW5,RW6,RW7) ");     \
175                 __asm(" MOVL A, _pxCurrentTCB ");                       \
176                 __asm(" MOVL RL2, A ");                                 \
177                 __asm(" MOVW A, SP ");                                  \
178                 __asm(" MOVW @RL2+0, A ");                              \
179                 __asm(" MOV A, USB ");                                  \
180                 __asm(" MOV @RL2+2, A ");                               \
181             }
182 
183     #define portRESTORE_CONTEXT()                                       \
184             {   __asm(" MOVL A, _pxCurrentTCB ");                       \
185                 __asm(" MOVL RL2, A ");                                 \
186                 __asm(" MOVW A, @RL2+0 ");                              \
187                 __asm(" AND  CCR,#H'DF ");                              \
188                 __asm(" MOVW SP, A ");                                  \
189                 __asm(" MOV A, @RL2+2 ");                               \
190                 __asm(" MOV USB, A ");                                  \
191                 __asm(" POPW (RW0,RW1,RW2,RW3,RW4,RW5,RW6,RW7) ");      \
192                 __asm(" POPW  A ");                                     \
193                 __asm(" OR   CCR,#H'20 ");                              \
194                 __asm(" PUSHW  A ");                                    \
195                 __asm(" AND  CCR,#H'DF ");                              \
196                 __asm(" POPW  A ");                                     \
197                 __asm(" OR   CCR,#H'20 ");                              \
198                 __asm(" PUSHW  A ");                                    \
199                 __asm(" AND  CCR,#H'DF ");                              \
200                 __asm(" POPW  A ");                                     \
201                 __asm(" OR   CCR,#H'20 ");                              \
202                 __asm(" PUSHW  A ");                                    \
203                 __asm(" AND  CCR,#H'DF ");                              \
204                 __asm(" POPW  A ");                                     \
205                 __asm(" OR   CCR,#H'20 ");                              \
206                 __asm(" PUSHW  A ");                                    \
207                 __asm(" AND  CCR,#H'DF ");                              \
208                 __asm(" POPW  A ");                                     \
209                 __asm(" OR   CCR,#H'20 ");                              \
210                 __asm(" PUSHW  A ");                                    \
211                 __asm(" AND  CCR,#H'DF ");                              \
212                 __asm(" POPW  A ");                                     \
213                 __asm(" OR   CCR,#H'20 ");                              \
214                 __asm(" PUSHW  A ");                                    \
215             }
216 #endif
217 
218 /*-----------------------------------------------------------*/
219 
220 /*
221  * Functions for obtaining the current value  of  DPR:ADB, DTB:PCB bank registers
222  */
223 
224 #pragma asm
225 
226         .GLOBAL    _xGet_DPR_ADB_bank
227         .GLOBAL    _xGet_DTB_PCB_bank
228         .SECTION   CODE, CODE, ALIGN=1
229 
230 _xGet_DPR_ADB_bank:
231 
232     MOV A, DPR
233     SWAP
234     MOV A, ADB
235     ORW A
236     #if configMEMMODEL == portMEDIUM || configMEMMODEL == portLARGE
237         RETP
238     #elif configMEMMODEL == portSMALL || configMEMMODEL == portCOMPACT
239         RET
240     #endif
241 
242 
243 _xGet_DTB_PCB_bank:
244 
245     MOV A, DTB
246     SWAP
247     MOV A, PCB
248     ORW A
249     #if configMEMMODEL == portMEDIUM || configMEMMODEL == portLARGE
250         RETP
251     #elif configMEMMODEL == portSMALL || configMEMMODEL == portCOMPACT
252         RET
253     #endif
254 
255 #pragma endasm
256 /*-----------------------------------------------------------*/
257 
258 /*
259  * Initialise the stack of a task to look exactly as if a call to
260  * portSAVE_CONTEXT had been called.
261  *
262  * See the header file portable.h.
263  */
264 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
265 {
266     /* Place a few bytes of known values on the bottom of the stack.
267     This is just useful for debugging. */
268     *pxTopOfStack = 0x1111;
269     pxTopOfStack--;
270     *pxTopOfStack = 0x2222;
271     pxTopOfStack--;
272     *pxTopOfStack = 0x3333;
273     pxTopOfStack--;
274 
275     /* Once the task is called the task  would  push  the  pointer to the
276     parameter onto the stack. Hence here the pointer would be copied to the stack
277     first.  When using the COMPACT or LARGE memory model the pointer would be 24
278     bits, and when using the SMALL or MEDIUM memory model the pointer would be 16
279     bits. */
280     #if( ( configMEMMODEL == portCOMPACT ) || ( configMEMMODEL == portLARGE ) )
281     {
282         *pxTopOfStack = ( StackType_t ) ( ( uint32_t ) ( pvParameters ) >> 16 );
283         pxTopOfStack--;
284     }
285     #endif
286 
287     *pxTopOfStack = ( StackType_t ) ( pvParameters );
288     pxTopOfStack--;
289 
290     /* This is redundant push to the stack. This is required in order to introduce
291     an offset so that the task accesses a parameter correctly that is passed on to
292     the task stack. */
293     #if( ( configMEMMODEL == portMEDIUM ) || ( configMEMMODEL == portLARGE ) )
294     {
295         *pxTopOfStack = ( xGet_DTB_PCB_bank() & 0xff00 ) | ( ( ( int32_t ) ( pxCode ) >> 16 ) & 0xff );
296         pxTopOfStack--;
297     }
298     #endif
299 
300     /* This is redundant push to the stack. This is required in order to introduce
301     an offset so the task correctly accesses the parameter passed on the task stack. */
302     *pxTopOfStack = ( StackType_t ) ( pxCode );
303     pxTopOfStack--;
304 
305     /* PS - User Mode, ILM=7, RB=0, Interrupts enabled,USP */
306     *pxTopOfStack = 0xE0C0;
307     pxTopOfStack--;
308 
309     /* PC */
310     *pxTopOfStack = ( StackType_t ) ( pxCode );
311     pxTopOfStack--;
312 
313     /* DTB | PCB */
314     #if configMEMMODEL == portSMALL || configMEMMODEL == portCOMPACT
315     {
316         *pxTopOfStack = xGet_DTB_PCB_bank();
317         pxTopOfStack--;
318     }
319     #endif
320 
321     /* DTB | PCB, in case of MEDIUM and LARGE memory models, PCB would be used
322     along with PC to indicate the start address of the function. */
323     #if( ( configMEMMODEL == portMEDIUM ) || ( configMEMMODEL == portLARGE ) )
324     {
325         *pxTopOfStack = ( xGet_DTB_PCB_bank() & 0xff00 ) | ( ( ( int32_t ) ( pxCode ) >> 16 ) & 0xff );
326         pxTopOfStack--;
327     }
328     #endif
329 
330     /* DPR | ADB  */
331     *pxTopOfStack = xGet_DPR_ADB_bank();
332     pxTopOfStack--;
333 
334     /* AL */
335     *pxTopOfStack = ( StackType_t ) 0x9999;
336     pxTopOfStack--;
337 
338     /* AH */
339     *pxTopOfStack = ( StackType_t ) 0xAAAA;
340     pxTopOfStack--;
341 
342     /* Next the general purpose registers. */
343     *pxTopOfStack = ( StackType_t ) 0x7777; /* RW7 */
344     pxTopOfStack--;
345     *pxTopOfStack = ( StackType_t ) 0x6666; /* RW6 */
346     pxTopOfStack--;
347     *pxTopOfStack = ( StackType_t ) 0x5555; /* RW5 */
348     pxTopOfStack--;
349     *pxTopOfStack = ( StackType_t ) 0x4444; /* RW4 */
350     pxTopOfStack--;
351     *pxTopOfStack = ( StackType_t ) 0x3333; /* RW3 */
352     pxTopOfStack--;
353     *pxTopOfStack = ( StackType_t ) 0x2222; /* RW2 */
354     pxTopOfStack--;
355     *pxTopOfStack = ( StackType_t ) 0x1111; /* RW1 */
356     pxTopOfStack--;
357     *pxTopOfStack = ( StackType_t ) 0x8888; /* RW0 */
358 
359     return pxTopOfStack;
360 }
361 /*-----------------------------------------------------------*/
362 
prvSetupRLT0Interrupt(void)363 static void prvSetupRLT0Interrupt( void )
364 {
365 /* The peripheral clock divided by 16 is used by the timer. */
366 const uint16_t usReloadValue = ( uint16_t ) ( ( ( configCLKP1_CLOCK_HZ / configTICK_RATE_HZ ) / 16UL ) - 1UL );
367 
368     /* set reload value = 34999+1, TICK Interrupt after 10 ms @ 56MHz of CLKP1 */
369     TMRLR0 = usReloadValue;
370 
371     /* prescaler 1:16, reload, interrupt enable, count enable, trigger */
372     TMCSR0 = 0x041B;
373 }
374 /*-----------------------------------------------------------*/
375 
xPortStartScheduler(void)376 BaseType_t xPortStartScheduler( void )
377 {
378     /* Setup the hardware to generate the tick. */
379     prvSetupRLT0Interrupt();
380 
381     /* Restore the context of the first task that is going to run. */
382     portRESTORE_CONTEXT();
383 
384     /* Simulate a function call end as generated by the compiler.  We will now
385     jump to the start of the task the context of which we have just restored. */
386     __asm(" reti ");
387 
388 
389     /* Should not get here. */
390     return pdTRUE;
391 }
392 /*-----------------------------------------------------------*/
393 
vPortEndScheduler(void)394 void vPortEndScheduler( void )
395 {
396     /* Not implemented - unlikely to ever be required as there is nothing to
397     return to. */
398 }
399 
400 /*-----------------------------------------------------------*/
401 
402 /*
403  * The interrupt service routine used depends on whether the pre-emptive
404  * scheduler is being used or not.
405  */
406 
407 #if configUSE_PREEMPTION == 1
408 
409     /*
410      * Tick ISR for preemptive scheduler.  We can use a __nosavereg attribute
411      * as the context is to be saved by the portSAVE_CONTEXT() macro, not the
412      * compiler generated code.  The tick count is incremented after the context
413      * is saved.
414      */
prvRLT0_TICKISR(void)415     __nosavereg __interrupt void prvRLT0_TICKISR( void )
416     {
417         /* Disable interrupts so that portSAVE_CONTEXT() is not interrupted */
418         __DI();
419 
420         /* Save the context of the interrupted task. */
421         portSAVE_CONTEXT();
422 
423         /* Enable interrupts */
424         __EI();
425 
426         /* Clear RLT0 interrupt flag */
427         TMCSR0_UF = 0;
428 
429         /* Increment the tick count then switch to the highest priority task
430         that is ready to run. */
431         if( xTaskIncrementTick() != pdFALSE )
432         {
433             vTaskSwitchContext();
434         }
435 
436         /* Disable interrupts so that portRESTORE_CONTEXT() is not interrupted */
437         __DI();
438 
439         /* Restore the context of the new task. */
440         portRESTORE_CONTEXT();
441 
442         /* Enable interrupts */
443         __EI();
444     }
445 
446 #else
447 
448     /*
449      * Tick ISR for the cooperative scheduler.  All this does is increment the
450      * tick count.  We don't need to switch context, this can only be done by
451      * manual calls to taskYIELD();
452      */
prvRLT0_TICKISR(void)453     __interrupt void prvRLT0_TICKISR( void )
454     {
455         /* Clear RLT0 interrupt flag */
456         TMCSR0_UF = 0;
457 
458         xTaskIncrementTick();
459     }
460 
461 #endif
462 
463 /*-----------------------------------------------------------*/
464 
465 /*
466  * Manual context switch. We can use a __nosavereg attribute  as the context
467  * is to be saved by the portSAVE_CONTEXT() macro, not the compiler generated
468  * code.
469  */
vPortYield(void)470 __nosavereg __interrupt void vPortYield( void )
471 {
472     /* Save the context of the interrupted task. */
473     portSAVE_CONTEXT();
474 
475     /* Switch to the highest priority task that is ready to run. */
476     vTaskSwitchContext();
477 
478     /* Restore the context of the new task. */
479     portRESTORE_CONTEXT();
480 }
481 /*-----------------------------------------------------------*/
482 
vPortYieldDelayed(void)483 __nosavereg __interrupt void vPortYieldDelayed( void )
484 {
485     /* Disable interrupts so that portSAVE_CONTEXT() is not interrupted */
486     __DI();
487 
488     /* Save the context of the interrupted task. */
489     portSAVE_CONTEXT();
490 
491     /* Enable interrupts */
492     __EI();
493 
494     /* Clear delayed interrupt flag */
495     __asm (" CLRB  03A4H:0 ");
496 
497     /* Switch to the highest priority task that is ready to run. */
498     vTaskSwitchContext();
499 
500     /* Disable interrupts so that portSAVE_CONTEXT() is not interrupted */
501     __DI();
502 
503     /* Restore the context of the new task. */
504     portRESTORE_CONTEXT();
505 
506     /* Enable interrupts */
507     __EI();
508 }
509 /*-----------------------------------------------------------*/
510