]> begriffs open source - freertos/blob - portable/Softune/MB96340/port.c
Make taskYIELD available to unprivileged tasks (#817)
[freertos] / portable / Softune / MB96340 / port.c
1 /*
2  * FreeRTOS Kernel <DEVELOPMENT BRANCH>
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
109  * effectively the reverse of SAVE_CONTEXT(). First the stack pointer
110  * value (USP for SMALL and MEDIUM memory model amd USB:USP for COMPACT
111  * and LARGE memory model ) is loaded from the task control block. Next the
112  * value of all the general purpose registers RW0-RW7 is retrieved.
113  * Finally it copies of the context ( AH:AL, DPR:ADB, DTB:PCB, PC and PS)
114  * of 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 /* if ( ( configMEMMODEL == portSMALL ) || ( configMEMMODEL == portMEDIUM ) ) */
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,
265                                      TaskFunction_t pxCode,
266                                      void * pvParameters )
267 {
268     /* Place a few bytes of known values on the bottom of the stack.
269      * This is just useful for debugging. */
270     *pxTopOfStack = 0x1111;
271     pxTopOfStack--;
272     *pxTopOfStack = 0x2222;
273     pxTopOfStack--;
274     *pxTopOfStack = 0x3333;
275     pxTopOfStack--;
276
277     /* Once the task is called the task would push the pointer to the
278      * parameter onto the stack. Hence here the pointer would be copied to the stack
279      * first. When using the COMPACT or LARGE memory model the pointer would be 24
280      * bits, and when using the SMALL or MEDIUM memory model the pointer would be 16
281      * bits. */
282     #if ( ( configMEMMODEL == portCOMPACT ) || ( configMEMMODEL == portLARGE ) )
283     {
284         *pxTopOfStack = ( StackType_t ) ( ( uint32_t ) ( pvParameters ) >> 16 );
285         pxTopOfStack--;
286     }
287     #endif
288
289     *pxTopOfStack = ( StackType_t ) ( pvParameters );
290     pxTopOfStack--;
291
292     /* This is redundant push to the stack. This is required in order to introduce
293      * an offset so that the task accesses a parameter correctly that is passed on to
294      * the task stack. */
295     #if ( ( configMEMMODEL == portMEDIUM ) || ( configMEMMODEL == portLARGE ) )
296     {
297         *pxTopOfStack = ( xGet_DTB_PCB_bank() & 0xff00 ) | ( ( ( int32_t ) ( pxCode ) >> 16 ) & 0xff );
298         pxTopOfStack--;
299     }
300     #endif
301
302     /* This is redundant push to the stack. This is required in order to introduce
303      * an offset so the task correctly accesses the parameter passed on the task stack. */
304     *pxTopOfStack = ( StackType_t ) ( pxCode );
305     pxTopOfStack--;
306
307     /* PS - User Mode, ILM=7, RB=0, Interrupts enabled,USP */
308     *pxTopOfStack = 0xE0C0;
309     pxTopOfStack--;
310
311     /* PC */
312     *pxTopOfStack = ( StackType_t ) ( pxCode );
313     pxTopOfStack--;
314
315     /* DTB | PCB */
316     #if configMEMMODEL == portSMALL || configMEMMODEL == portCOMPACT
317     {
318         *pxTopOfStack = xGet_DTB_PCB_bank();
319         pxTopOfStack--;
320     }
321     #endif
322
323     /* DTB | PCB, in case of MEDIUM and LARGE memory models, PCB would be used
324      * along with PC to indicate the start address of the function. */
325     #if ( ( configMEMMODEL == portMEDIUM ) || ( configMEMMODEL == portLARGE ) )
326     {
327         *pxTopOfStack = ( xGet_DTB_PCB_bank() & 0xff00 ) | ( ( ( int32_t ) ( pxCode ) >> 16 ) & 0xff );
328         pxTopOfStack--;
329     }
330     #endif
331
332     /* DPR | ADB */
333     *pxTopOfStack = xGet_DPR_ADB_bank();
334     pxTopOfStack--;
335
336     /* AL */
337     *pxTopOfStack = ( StackType_t ) 0x9999;
338     pxTopOfStack--;
339
340     /* AH */
341     *pxTopOfStack = ( StackType_t ) 0xAAAA;
342     pxTopOfStack--;
343
344     /* Next the general purpose registers. */
345     *pxTopOfStack = ( StackType_t ) 0x7777; /* RW7 */
346     pxTopOfStack--;
347     *pxTopOfStack = ( StackType_t ) 0x6666; /* RW6 */
348     pxTopOfStack--;
349     *pxTopOfStack = ( StackType_t ) 0x5555; /* RW5 */
350     pxTopOfStack--;
351     *pxTopOfStack = ( StackType_t ) 0x4444; /* RW4 */
352     pxTopOfStack--;
353     *pxTopOfStack = ( StackType_t ) 0x3333; /* RW3 */
354     pxTopOfStack--;
355     *pxTopOfStack = ( StackType_t ) 0x2222; /* RW2 */
356     pxTopOfStack--;
357     *pxTopOfStack = ( StackType_t ) 0x1111; /* RW1 */
358     pxTopOfStack--;
359     *pxTopOfStack = ( StackType_t ) 0x8888; /* RW0 */
360
361     return pxTopOfStack;
362 }
363 /*-----------------------------------------------------------*/
364
365 static void prvSetupRLT0Interrupt( void )
366 {
367 /* The peripheral clock divided by 16 is used by the timer. */
368     const uint16_t usReloadValue = ( uint16_t ) ( ( ( configCLKP1_CLOCK_HZ / configTICK_RATE_HZ ) / 16UL ) - 1UL );
369
370     /* set reload value = 34999+1, TICK Interrupt after 10 ms @ 56MHz of CLKP1 */
371     TMRLR0 = usReloadValue;
372
373     /* prescaler 1:16, reload, interrupt enable, count enable, trigger */
374     TMCSR0 = 0x041B;
375 }
376 /*-----------------------------------------------------------*/
377
378 BaseType_t xPortStartScheduler( void )
379 {
380     /* Setup the hardware to generate the tick. */
381     prvSetupRLT0Interrupt();
382
383     /* Restore the context of the first task that is going to run. */
384     portRESTORE_CONTEXT();
385
386     /* Simulate a function call end as generated by the compiler. We will now
387      * jump to the start of the task the context of which we have just restored. */
388     __asm( " reti " );
389
390
391     /* Should not get here. */
392     return pdTRUE;
393 }
394 /*-----------------------------------------------------------*/
395
396 void vPortEndScheduler( void )
397 {
398     /* Not implemented - unlikely to ever be required as there is nothing to
399      * return to. */
400 }
401
402 /*-----------------------------------------------------------*/
403
404 /*
405  * The interrupt service routine used depends on whether the pre-emptive
406  * scheduler is being used or not.
407  */
408
409 #if configUSE_PREEMPTION == 1
410
411 /*
412  * Tick ISR for preemptive scheduler. We can use a __nosavereg attribute
413  * as the context is to be saved by the portSAVE_CONTEXT() macro, not the
414  * compiler generated code. The tick count is incremented after the context
415  * is saved.
416  */
417     __nosavereg __interrupt void prvRLT0_TICKISR( void )
418     {
419         /* Disable interrupts so that portSAVE_CONTEXT() is not interrupted */
420         __DI();
421
422         /* Save the context of the interrupted task. */
423         portSAVE_CONTEXT();
424
425         /* Enable interrupts */
426         __EI();
427
428         /* Clear RLT0 interrupt flag */
429         TMCSR0_UF = 0;
430
431         /* Increment the tick count then switch to the highest priority task
432          * that is ready to run. */
433         if( xTaskIncrementTick() != pdFALSE )
434         {
435             vTaskSwitchContext();
436         }
437
438         /* Disable interrupts so that portRESTORE_CONTEXT() is not interrupted */
439         __DI();
440
441         /* Restore the context of the new task. */
442         portRESTORE_CONTEXT();
443
444         /* Enable interrupts */
445         __EI();
446     }
447
448 #else /* if configUSE_PREEMPTION == 1 */
449
450 /*
451  * Tick ISR for the cooperative scheduler. All this does is increment the
452  * tick count. We don't need to switch context, this can only be done by
453  * manual calls to taskYIELD();
454  */
455     __interrupt void prvRLT0_TICKISR( void )
456     {
457         /* Clear RLT0 interrupt flag */
458         TMCSR0_UF = 0;
459
460         xTaskIncrementTick();
461     }
462
463 #endif /* if configUSE_PREEMPTION == 1 */
464
465 /*-----------------------------------------------------------*/
466
467 /*
468  * Manual context switch. We can use a __nosavereg attribute as the context
469  * is to be saved by the portSAVE_CONTEXT() macro, not the compiler generated
470  * code.
471  */
472 __nosavereg __interrupt void vPortYield( void )
473 {
474     /* Save the context of the interrupted task. */
475     portSAVE_CONTEXT();
476
477     /* Switch to the highest priority task that is ready to run. */
478     vTaskSwitchContext();
479
480     /* Restore the context of the new task. */
481     portRESTORE_CONTEXT();
482 }
483 /*-----------------------------------------------------------*/
484
485 __nosavereg __interrupt void vPortYieldDelayed( void )
486 {
487     /* Disable interrupts so that portSAVE_CONTEXT() is not interrupted */
488     __DI();
489
490     /* Save the context of the interrupted task. */
491     portSAVE_CONTEXT();
492
493     /* Enable interrupts */
494     __EI();
495
496     /* Clear delayed interrupt flag */
497     __asm( " CLRB  03A4H:0 " );
498
499     /* Switch to the highest priority task that is ready to run. */
500     vTaskSwitchContext();
501
502     /* Disable interrupts so that portSAVE_CONTEXT() is not interrupted */
503     __DI();
504
505     /* Restore the context of the new task. */
506     portRESTORE_CONTEXT();
507
508     /* Enable interrupts */
509     __EI();
510 }
511 /*-----------------------------------------------------------*/