2 * FreeRTOS Kernel <DEVELOPMENT BRANCH>
3 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 * SPDX-License-Identifier: MIT
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:
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
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.
24 * https://www.FreeRTOS.org
25 * https://github.com/FreeRTOS
32 /*-----------------------------------------------------------
33 * Implementation of functions defined in portable.h for the 16FX port.
34 *----------------------------------------------------------*/
37 * Get current value of DPR and ADB registers
39 StackType_t xGet_DPR_ADB_bank( void );
42 * Get current value of DTB and PCB registers
44 StackType_t xGet_DTB_PCB_bank( void );
47 * Sets up the periodic ISR used for the RTOS tick. This uses RLT0, but
48 * can be done using any given RLT.
50 static void prvSetupRLT0Interrupt( void );
52 /*-----------------------------------------------------------*/
55 * We require the address of the pxCurrentTCB variable, but don't want to know
56 * any details of its type.
59 extern volatile TCB_t * volatile pxCurrentTCB;
61 /*-----------------------------------------------------------*/
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
73 #if ( ( configMEMMODEL == portSMALL ) || ( configMEMMODEL == portMEDIUM ) )
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 " ); \
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.
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 " ); \
148 #elif ( ( configMEMMODEL == portCOMPACT ) || ( configMEMMODEL == portLARGE ) )
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 " ); \
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 " ); \
216 #endif /* if ( ( configMEMMODEL == portSMALL ) || ( configMEMMODEL == portMEDIUM ) ) */
218 /*-----------------------------------------------------------*/
221 * Functions for obtaining the current value of DPR:ADB, DTB:PCB bank registers
226 .GLOBAL _xGet_DPR_ADB_bank
227 .GLOBAL _xGet_DTB_PCB_bank
228 .SECTION CODE, CODE, ALIGN=1
236 #if configMEMMODEL == portMEDIUM || configMEMMODEL == portLARGE
238 #elif configMEMMODEL == portSMALL || configMEMMODEL == portCOMPACT
249 #if configMEMMODEL == portMEDIUM || configMEMMODEL == portLARGE
251 #elif configMEMMODEL == portSMALL || configMEMMODEL == portCOMPACT
256 /*-----------------------------------------------------------*/
259 * Initialise the stack of a task to look exactly as if a call to
260 * portSAVE_CONTEXT had been called.
262 * See the header file portable.h.
264 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
265 TaskFunction_t pxCode,
266 void * pvParameters )
268 /* Place a few bytes of known values on the bottom of the stack.
269 * This is just useful for debugging. */
270 *pxTopOfStack = 0x1111;
272 *pxTopOfStack = 0x2222;
274 *pxTopOfStack = 0x3333;
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
282 #if ( ( configMEMMODEL == portCOMPACT ) || ( configMEMMODEL == portLARGE ) )
284 *pxTopOfStack = ( StackType_t ) ( ( uint32_t ) ( pvParameters ) >> 16 );
289 *pxTopOfStack = ( StackType_t ) ( pvParameters );
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
295 #if ( ( configMEMMODEL == portMEDIUM ) || ( configMEMMODEL == portLARGE ) )
297 *pxTopOfStack = ( xGet_DTB_PCB_bank() & 0xff00 ) | ( ( ( int32_t ) ( pxCode ) >> 16 ) & 0xff );
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 );
307 /* PS - User Mode, ILM=7, RB=0, Interrupts enabled,USP */
308 *pxTopOfStack = 0xE0C0;
312 *pxTopOfStack = ( StackType_t ) ( pxCode );
316 #if configMEMMODEL == portSMALL || configMEMMODEL == portCOMPACT
318 *pxTopOfStack = xGet_DTB_PCB_bank();
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 ) )
327 *pxTopOfStack = ( xGet_DTB_PCB_bank() & 0xff00 ) | ( ( ( int32_t ) ( pxCode ) >> 16 ) & 0xff );
333 *pxTopOfStack = xGet_DPR_ADB_bank();
337 *pxTopOfStack = ( StackType_t ) 0x9999;
341 *pxTopOfStack = ( StackType_t ) 0xAAAA;
344 /* Next the general purpose registers. */
345 *pxTopOfStack = ( StackType_t ) 0x7777; /* RW7 */
347 *pxTopOfStack = ( StackType_t ) 0x6666; /* RW6 */
349 *pxTopOfStack = ( StackType_t ) 0x5555; /* RW5 */
351 *pxTopOfStack = ( StackType_t ) 0x4444; /* RW4 */
353 *pxTopOfStack = ( StackType_t ) 0x3333; /* RW3 */
355 *pxTopOfStack = ( StackType_t ) 0x2222; /* RW2 */
357 *pxTopOfStack = ( StackType_t ) 0x1111; /* RW1 */
359 *pxTopOfStack = ( StackType_t ) 0x8888; /* RW0 */
363 /*-----------------------------------------------------------*/
365 static void prvSetupRLT0Interrupt( void )
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 );
370 /* set reload value = 34999+1, TICK Interrupt after 10 ms @ 56MHz of CLKP1 */
371 TMRLR0 = usReloadValue;
373 /* prescaler 1:16, reload, interrupt enable, count enable, trigger */
376 /*-----------------------------------------------------------*/
378 BaseType_t xPortStartScheduler( void )
380 /* Setup the hardware to generate the tick. */
381 prvSetupRLT0Interrupt();
383 /* Restore the context of the first task that is going to run. */
384 portRESTORE_CONTEXT();
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. */
391 /* Should not get here. */
394 /*-----------------------------------------------------------*/
396 void vPortEndScheduler( void )
398 /* Not implemented - unlikely to ever be required as there is nothing to
402 /*-----------------------------------------------------------*/
405 * The interrupt service routine used depends on whether the pre-emptive
406 * scheduler is being used or not.
409 #if configUSE_PREEMPTION == 1
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
417 __nosavereg __interrupt void prvRLT0_TICKISR( void )
419 /* Disable interrupts so that portSAVE_CONTEXT() is not interrupted */
422 /* Save the context of the interrupted task. */
425 /* Enable interrupts */
428 /* Clear RLT0 interrupt flag */
431 /* Increment the tick count then switch to the highest priority task
432 * that is ready to run. */
433 if( xTaskIncrementTick() != pdFALSE )
435 vTaskSwitchContext();
438 /* Disable interrupts so that portRESTORE_CONTEXT() is not interrupted */
441 /* Restore the context of the new task. */
442 portRESTORE_CONTEXT();
444 /* Enable interrupts */
448 #else /* if configUSE_PREEMPTION == 1 */
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();
455 __interrupt void prvRLT0_TICKISR( void )
457 /* Clear RLT0 interrupt flag */
460 xTaskIncrementTick();
463 #endif /* if configUSE_PREEMPTION == 1 */
465 /*-----------------------------------------------------------*/
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
472 __nosavereg __interrupt void vPortYield( void )
474 /* Save the context of the interrupted task. */
477 /* Switch to the highest priority task that is ready to run. */
478 vTaskSwitchContext();
480 /* Restore the context of the new task. */
481 portRESTORE_CONTEXT();
483 /*-----------------------------------------------------------*/
485 __nosavereg __interrupt void vPortYieldDelayed( void )
487 /* Disable interrupts so that portSAVE_CONTEXT() is not interrupted */
490 /* Save the context of the interrupted task. */
493 /* Enable interrupts */
496 /* Clear delayed interrupt flag */
497 __asm( " CLRB 03A4H:0 " );
499 /* Switch to the highest priority task that is ready to run. */
500 vTaskSwitchContext();
502 /* Disable interrupts so that portSAVE_CONTEXT() is not interrupted */
505 /* Restore the context of the new task. */
506 portRESTORE_CONTEXT();
508 /* Enable interrupts */
511 /*-----------------------------------------------------------*/