]> begriffs open source - cmsis-freertos/blob - Source/portable/ThirdParty/XCC/Xtensa/portasm.S
Update FreeRTOS kernel to v11.1.0
[cmsis-freertos] / Source / portable / ThirdParty / XCC / Xtensa / portasm.S
1 /*
2  * FreeRTOS Kernel V11.1.0
3  * Copyright (C) 2015-2019 Cadence Design Systems, Inc.
4  * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5  *
6  * SPDX-License-Identifier: MIT
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy of
9  * this software and associated documentation files (the "Software"), to deal in
10  * the Software without restriction, including without limitation the rights to
11  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
12  * the Software, and to permit persons to whom the Software is furnished to do so,
13  * subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in all
16  * copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
20  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
21  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
22  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  *
25  * https://www.FreeRTOS.org
26  * https://github.com/FreeRTOS
27  *
28  */
29
30 #include "xtensa_rtos.h"
31
32 #define TOPOFSTACK_OFFS                 0x00    /* StackType_t *pxTopOfStack */
33 #define CP_TOPOFSTACK_OFFS              0x04    /* xMPU_SETTINGS.coproc_area */
34
35 .extern pxCurrentTCB
36
37
38 /*
39 *******************************************************************************
40 * Interrupt stack. The size of the interrupt stack is determined by the config
41 * parameter "configISR_STACK_SIZE" in FreeRTOSConfig.h
42 *******************************************************************************
43 */
44     .data
45     .align      16
46     .global     port_IntStack
47 port_IntStack:
48     .space      configISR_STACK_SIZE
49 port_IntStackTop:
50     .word       0
51 port_switch_flag:
52     .word       0
53
54     .text
55 /*
56 *******************************************************************************
57 * _frxt_setup_switch
58 * void _frxt_setup_switch(void);
59 *
60 * Sets an internal flag indicating that a task switch is required on return
61 * from interrupt handling.
62 *
63 *******************************************************************************
64 */
65     .global     _frxt_setup_switch
66     .type       _frxt_setup_switch,@function
67     .align      4
68 _frxt_setup_switch:
69
70     ENTRY(16)
71
72     movi    a2, port_switch_flag
73     movi    a3, 1
74     s32i    a3, a2, 0
75
76     RET(16)
77
78 /*
79 *******************************************************************************
80 *                                            _frxt_int_enter
81 *                                       void _frxt_int_enter(void)
82 *
83 * Implements the Xtensa RTOS porting layer's XT_RTOS_INT_ENTER function for
84 * freeRTOS. Saves the rest of the interrupt context (not already saved).
85 * May only be called from assembly code by the 'call0' instruction, with
86 * interrupts disabled.
87 * See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
88 *
89 *******************************************************************************
90 */
91     .globl  _frxt_int_enter
92     .type   _frxt_int_enter,@function
93     .align  4
94 _frxt_int_enter:
95
96     /* Save a12-13 in the stack frame as required by _xt_context_save. */
97     s32i    a12, a1, XT_STK_A12
98     s32i    a13, a1, XT_STK_A13
99
100     /* Save return address in a safe place (free a0). */
101     mov     a12, a0
102
103     /* Save the rest of the interrupted context (preserves A12-13). */
104     call0   _xt_context_save
105
106     /*
107     Save interrupted task's SP in TCB only if not nesting.
108     Manage nesting directly rather than call the generic IntEnter()
109     (in windowed ABI we can't call a C function here anyway because PS.EXCM is still set).
110     */
111     movi    a2,  port_xSchedulerRunning
112     movi    a3,  port_interruptNesting
113     l32i    a2,  a2, 0                  /* a2 = port_xSchedulerRunning     */
114     beqz    a2,  1f                     /* scheduler not running, no tasks */
115     l32i    a2,  a3, 0                  /* a2 = port_interruptNesting      */
116     addi    a2,  a2, 1                  /* increment nesting count         */
117     s32i    a2,  a3, 0                  /* save nesting count              */
118     bnei    a2,  1, .Lnested            /* !=0 before incr, so nested      */
119
120     movi    a2,  pxCurrentTCB
121     l32i    a2,  a2, 0                  /* a2 = current TCB                */
122     beqz    a2,  1f
123     s32i    a1,  a2, TOPOFSTACK_OFFS    /* pxCurrentTCB->pxTopOfStack = SP */
124     movi    a1,  port_IntStackTop       /* a1 = top of intr stack          */
125
126 .Lnested:
127 1:
128     mov     a0,  a12                    /* restore return addr and return  */
129     ret
130
131 /*
132 *******************************************************************************
133 *                                            _frxt_int_exit
134 *                                       void _frxt_int_exit(void)
135 *
136 * Implements the Xtensa RTOS porting layer's XT_RTOS_INT_EXIT function for
137 * FreeRTOS. If required, calls vPortYieldFromInt() to perform task context
138 * switching, restore the (possibly) new task's context, and return to the
139 * exit dispatcher saved in the task's stack frame at XT_STK_EXIT.
140 * May only be called from assembly code by the 'call0' instruction. Does not
141 * return to caller.
142 * See the description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
143 *
144 *******************************************************************************
145 */
146     .globl  _frxt_int_exit
147     .type   _frxt_int_exit,@function
148     .align  4
149 _frxt_int_exit:
150
151     movi    a2,  port_xSchedulerRunning
152     movi    a3,  port_interruptNesting
153     rsil    a0,  XCHAL_EXCM_LEVEL       /* lock out interrupts             */
154     l32i    a2,  a2, 0                  /* a2 = port_xSchedulerRunning     */
155     beqz    a2,  .Lnoswitch             /* scheduler not running, no tasks */
156     l32i    a2,  a3, 0                  /* a2 = port_interruptNesting      */
157     addi    a2,  a2, -1                 /* decrement nesting count         */
158     s32i    a2,  a3, 0                  /* save nesting count              */
159     bnez    a2,  .Lnesting              /* !=0 after decr so still nested  */
160
161     movi    a2,  pxCurrentTCB
162     l32i    a2,  a2, 0                  /* a2 = current TCB                */
163     beqz    a2,  1f                     /* no task ? go to dispatcher      */
164     l32i    a1,  a2, TOPOFSTACK_OFFS    /* SP = pxCurrentTCB->pxTopOfStack */
165
166     movi    a2,  port_switch_flag       /* address of switch flag          */
167     l32i    a3,  a2, 0                  /* a3 = port_switch_flag           */
168     beqz    a3,  .Lnoswitch             /* flag = 0 means no switch reqd   */
169     movi    a3,  0
170     s32i    a3,  a2, 0                  /* zero out the flag for next time */
171
172 1:
173     /*
174     Call0 ABI callee-saved regs a12-15 need to be saved before possible preemption.
175     However a12-13 were already saved by _frxt_int_enter().
176     */
177     #ifdef __XTENSA_CALL0_ABI__
178     s32i    a14, a1, XT_STK_A14
179     s32i    a15, a1, XT_STK_A15
180     #endif
181
182     #ifdef __XTENSA_CALL0_ABI__
183     call0   vPortYieldFromInt       /* call dispatch inside the function; never returns */
184     #else
185     call4   vPortYieldFromInt       /* this one returns */
186     call0   _frxt_dispatch          /* tail-call dispatcher */
187     /* Never returns here. */
188     #endif
189
190 .Lnoswitch:
191     /*
192     If we came here then about to resume the interrupted task.
193     */
194
195 .Lnesting:
196     /*
197     We come here only if there was no context switch, that is if this
198     is a nested interrupt, or the interrupted task was not preempted.
199     In either case there's no need to load the SP.
200     */
201
202     /* Restore full context from interrupt stack frame */
203     call0   _xt_context_restore
204
205     /*
206     Must return via the exit dispatcher corresponding to the entrypoint from which
207     this was called. Interruptee's A0, A1, PS, PC are restored and the interrupt
208     stack frame is deallocated in the exit dispatcher.
209     */
210     l32i    a0,  a1, XT_STK_EXIT
211     ret
212
213
214 /*
215 **********************************************************************************************************
216 *                                           _frxt_timer_int
217 *                                      void _frxt_timer_int(void)
218 *
219 * Implements the Xtensa RTOS porting layer's XT_RTOS_TIMER_INT function for FreeRTOS.
220 * Called every timer interrupt.
221 * Manages the tick timer and calls xPortSysTickHandler() every tick.
222 * See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
223 *
224 * Callable from C (obeys ABI conventions). Implemented in assmebly code for performance.
225 *
226 **********************************************************************************************************
227 */
228     .globl  _frxt_timer_int
229     .type   _frxt_timer_int,@function
230     .align  4
231 _frxt_timer_int:
232
233     /*
234     Xtensa timers work by comparing a cycle counter with a preset value.  Once the match occurs
235     an interrupt is generated, and the handler has to set a new cycle count into the comparator.
236     To avoid clock drift due to interrupt latency, the new cycle count is computed from the old,
237     not the time the interrupt was serviced. However if a timer interrupt is ever serviced more
238     than one tick late, it is necessary to process multiple ticks until the new cycle count is
239     in the future, otherwise the next timer interrupt would not occur until after the cycle
240     counter had wrapped (2^32 cycles later).
241
242     do {
243         ticks++;
244         old_ccompare = read_ccompare_i();
245         write_ccompare_i( old_ccompare + divisor );
246         service one tick;
247         diff = read_ccount() - old_ccompare;
248     } while ( diff > divisor );
249     */
250
251     ENTRY(16)
252
253 .L_xt_timer_int_catchup:
254
255     /* Update the timer comparator for the next tick. */
256     #ifdef XT_CLOCK_FREQ
257     movi    a2, XT_TICK_DIVISOR         /* a2 = comparator increment          */
258     #else
259     movi    a3, _xt_tick_divisor
260     l32i    a2, a3, 0                   /* a2 = comparator increment          */
261     #endif
262     rsr     a3, XT_CCOMPARE             /* a3 = old comparator value          */
263     add     a4, a3, a2                  /* a4 = new comparator value          */
264     wsr     a4, XT_CCOMPARE             /* update comp. and clear interrupt   */
265     esync
266
267     #ifdef __XTENSA_CALL0_ABI__
268     /* Preserve a2 and a3 across C calls. */
269     s32i    a2, sp, 4
270     s32i    a3, sp, 8
271     #endif
272
273     /* Call the FreeRTOS tick handler (see port.c). */
274     #ifdef __XTENSA_CALL0_ABI__
275     call0   xPortSysTickHandler
276     #else
277     call4   xPortSysTickHandler
278     #endif
279
280     #ifdef __XTENSA_CALL0_ABI__
281     /* Restore a2 and a3. */
282     l32i    a2, sp, 4
283     l32i    a3, sp, 8
284     #endif
285
286     /* Check if we need to process more ticks to catch up. */
287     esync                               /* ensure comparator update complete  */
288     rsr     a4, CCOUNT                  /* a4 = cycle count                   */
289     sub     a4, a4, a3                  /* diff = ccount - old comparator     */
290     blt     a2, a4, .L_xt_timer_int_catchup  /* repeat while diff > divisor */
291
292     RET(16)
293
294     /*
295 **********************************************************************************************************
296 *                                           _frxt_tick_timer_init
297 *                                      void _frxt_tick_timer_init(void)
298 *
299 * Initialize timer and timer interrrupt handler (_xt_tick_divisor_init() has already been been called).
300 * Callable from C (obeys ABI conventions on entry).
301 *
302 **********************************************************************************************************
303 */
304     .globl  _frxt_tick_timer_init
305     .type   _frxt_tick_timer_init,@function
306     .align  4
307 _frxt_tick_timer_init:
308
309     ENTRY(16)
310
311     /* Set up the periodic tick timer (assume enough time to complete init). */
312     #ifdef XT_CLOCK_FREQ
313     movi    a3, XT_TICK_DIVISOR
314     #else
315     movi    a2, _xt_tick_divisor
316     l32i    a3, a2, 0
317     #endif
318     rsr     a2, CCOUNT              /* current cycle count */
319     add     a2, a2, a3              /* time of first timer interrupt */
320     wsr     a2, XT_CCOMPARE         /* set the comparator */
321
322     /*
323     Enable the timer interrupt at the device level. Don't write directly
324     to the INTENABLE register because it may be virtualized.
325     */
326     #ifdef __XTENSA_CALL0_ABI__
327     movi    a2, XT_TIMER_INTEN
328     call0   xt_ints_on
329     #else
330     movi    a6, XT_TIMER_INTEN
331     call4   xt_ints_on
332     #endif
333
334     RET(16)
335
336 /*
337 **********************************************************************************************************
338 *                                    DISPATCH THE HIGH READY TASK
339 *                                     void _frxt_dispatch(void)
340 *
341 * Switch context to the highest priority ready task, restore its state and dispatch control to it.
342 *
343 * This is a common dispatcher that acts as a shared exit path for all the context switch functions
344 * including vPortYield() and vPortYieldFromInt(), all of which tail-call this dispatcher
345 * (for windowed ABI vPortYieldFromInt() calls it indirectly via _frxt_int_exit() ).
346 *
347 * The Xtensa port uses different stack frames for solicited and unsolicited task suspension (see
348 * comments on stack frames in xtensa_context.h). This function restores the state accordingly.
349 * If restoring a task that solicited entry, restores the minimal state and leaves CPENABLE clear.
350 * If restoring a task that was preempted, restores all state including the task's CPENABLE.
351 *
352 * Entry:
353 *   pxCurrentTCB  points to the TCB of the task to suspend,
354 *   Because it is tail-called without a true function entrypoint, it needs no 'entry' instruction.
355 *
356 * Exit:
357 *   If incoming task called vPortYield() (solicited), this function returns as if from vPortYield().
358 *   If incoming task was preempted by an interrupt, this function jumps to exit dispatcher.
359 *
360 **********************************************************************************************************
361 */
362     .globl  _frxt_dispatch
363     .type   _frxt_dispatch,@function
364     .align  4
365 _frxt_dispatch:
366
367     #ifdef __XTENSA_CALL0_ABI__
368     call0   vTaskSwitchContext  // Get next TCB to resume
369     movi    a2, pxCurrentTCB
370     #else
371     movi    a2, pxCurrentTCB
372     call4   vTaskSwitchContext  // Get next TCB to resume
373     #endif
374     l32i    a3,  a2, 0
375     l32i    sp,  a3, TOPOFSTACK_OFFS     /* SP = next_TCB->pxTopOfStack;  */
376     s32i    a3,  a2, 0
377
378     /* Determine the type of stack frame. */
379     l32i    a2,  sp, XT_STK_EXIT        /* exit dispatcher or solicited flag */
380     bnez    a2,  .L_frxt_dispatch_stk
381
382 .L_frxt_dispatch_sol:
383
384     /* Solicited stack frame. Restore minimal context and return from vPortYield(). */
385     l32i    a3,  sp, XT_SOL_PS
386     #ifdef __XTENSA_CALL0_ABI__
387     l32i    a12, sp, XT_SOL_A12
388     l32i    a13, sp, XT_SOL_A13
389     l32i    a14, sp, XT_SOL_A14
390     l32i    a15, sp, XT_SOL_A15
391     #endif
392     l32i    a0,  sp, XT_SOL_PC
393     #if XCHAL_CP_NUM > 0
394     /* Ensure wsr.CPENABLE is complete (should be, it was cleared on entry). */
395     rsync
396     #endif
397     /* As soons as PS is restored, interrupts can happen. No need to sync PS. */
398     wsr     a3,  PS
399     #ifdef __XTENSA_CALL0_ABI__
400     addi    sp,  sp, XT_SOL_FRMSZ
401     ret
402     #else
403     retw
404     #endif
405
406 .L_frxt_dispatch_stk:
407
408     #if XCHAL_CP_NUM > 0
409     /* Restore CPENABLE from task's co-processor save area. */
410     movi    a3, pxCurrentTCB            /* cp_state =                       */
411     l32i    a3, a3, 0
412     l32i    a2, a3, CP_TOPOFSTACK_OFFS     /* StackType_t                       *pxStack; */
413     l16ui    a3, a2, XT_CPENABLE         /* CPENABLE = cp_state->cpenable;   */
414     wsr     a3, CPENABLE
415     #endif
416
417     /* Interrupt stack frame. Restore full context and return to exit dispatcher. */
418     call0   _xt_context_restore
419
420     /* In Call0 ABI, restore callee-saved regs (A12, A13 already restored). */
421     #ifdef __XTENSA_CALL0_ABI__
422     l32i    a14, sp, XT_STK_A14
423     l32i    a15, sp, XT_STK_A15
424     #endif
425
426     #if XCHAL_CP_NUM > 0
427     /* Ensure wsr.CPENABLE has completed. */
428     rsync
429     #endif
430
431     /*
432     Must return via the exit dispatcher corresponding to the entrypoint from which
433     this was called. Interruptee's A0, A1, PS, PC are restored and the interrupt
434     stack frame is deallocated in the exit dispatcher.
435     */
436     l32i    a0, sp, XT_STK_EXIT
437     ret
438
439
440 /*
441 **********************************************************************************************************
442 *                            PERFORM A SOLICTED CONTEXT SWITCH (from a task)
443 *                                        void vPortYield(void)
444 *
445 * This function saves the minimal state needed for a solicited task suspension, clears CPENABLE,
446 * then tail-calls the dispatcher _frxt_dispatch() to perform the actual context switch
447 *
448 * At Entry:
449 *   pxCurrentTCB  points to the TCB of the task to suspend
450 *   Callable from C (obeys ABI conventions on entry).
451 *
452 * Does not return to caller.
453 *
454 **********************************************************************************************************
455 */
456     .globl  vPortYield
457     .type   vPortYield,@function
458     .align  4
459 vPortYield:
460
461     #ifdef __XTENSA_CALL0_ABI__
462     addi    sp,  sp, -XT_SOL_FRMSZ
463     #else
464     entry   sp,  XT_SOL_FRMSZ
465     #endif
466
467     rsr     a2,  PS
468     s32i    a0,  sp, XT_SOL_PC
469     s32i    a2,  sp, XT_SOL_PS
470     #ifdef __XTENSA_CALL0_ABI__
471     s32i    a12, sp, XT_SOL_A12         /* save callee-saved registers      */
472     s32i    a13, sp, XT_SOL_A13
473     s32i    a14, sp, XT_SOL_A14
474     s32i    a15, sp, XT_SOL_A15
475     #else
476     /* Spill register windows. Calling xthal_window_spill() causes extra    */
477     /* spills and reloads, so we will set things up to call the _nw version */
478     /* instead to save cycles.                                              */
479     movi    a6,  ~(PS_WOE_MASK|PS_INTLEVEL_MASK)  /* spills a4-a7 if needed */
480     and     a2,  a2, a6                           /* clear WOE, INTLEVEL    */
481     addi    a2,  a2, XCHAL_EXCM_LEVEL             /* set INTLEVEL           */
482     wsr     a2,  PS
483     rsync
484     call0   xthal_window_spill_nw
485     l32i    a2,  sp, XT_SOL_PS                    /* restore PS             */
486     wsr     a2,  PS
487     #endif
488
489     rsil    a2,  XCHAL_EXCM_LEVEL       /* disable low/med interrupts       */
490
491     #if XCHAL_CP_NUM > 0
492     /* Save coprocessor callee-saved state (if any). At this point CPENABLE */
493     /* should still reflect which CPs were in use (enabled).                */
494     call0   _xt_coproc_savecs
495     #endif
496
497     movi    a2,  pxCurrentTCB
498     movi    a3,  0
499     l32i    a2,  a2, 0                  /* a2 = pxCurrentTCB                */
500     s32i    a3,  sp, XT_SOL_EXIT        /* 0 to flag as solicited frame     */
501     s32i    sp,  a2, TOPOFSTACK_OFFS    /* pxCurrentTCB->pxTopOfStack = SP  */
502
503     #if XCHAL_CP_NUM > 0
504     /* Clear CPENABLE, also in task's co-processor state save area. */
505     l32i    a2,  a2, CP_TOPOFSTACK_OFFS /* a2 = pxCurrentTCB->cp_state      */
506     movi    a3,  0
507     wsr     a3,  CPENABLE
508     beqz    a2,  1f
509     s16i     a3,  a2, XT_CPENABLE        /* clear saved cpenable             */
510 1:
511     #endif
512
513     /* Tail-call dispatcher. */
514     call0   _frxt_dispatch
515     /* Never reaches here. */
516
517
518 /*
519 **********************************************************************************************************
520 *                         PERFORM AN UNSOLICITED CONTEXT SWITCH (from an interrupt)
521 *                                        void vPortYieldFromInt(void)
522 *
523 * This calls the context switch hook (removed), saves and clears CPENABLE, then tail-calls the dispatcher
524 * _frxt_dispatch() to perform the actual context switch.
525 *
526 * At Entry:
527 *   Interrupted task context has been saved in an interrupt stack frame at pxCurrentTCB->pxTopOfStack.
528 *   pxCurrentTCB  points to the TCB of the task to suspend,
529 *   Callable from C (obeys ABI conventions on entry).
530 *
531 * At Exit:
532 *   Windowed ABI defers the actual context switch until the stack is unwound to interrupt entry.
533 *   Call0 ABI tail-calls the dispatcher directly (no need to unwind) so does not return to caller.
534 *
535 **********************************************************************************************************
536 */
537     .globl  vPortYieldFromInt
538     .type   vPortYieldFromInt,@function
539     .align  4
540 vPortYieldFromInt:
541
542     ENTRY(16)
543
544     #if XCHAL_CP_NUM > 0
545     /* Save CPENABLE in task's co-processor save area, and clear CPENABLE.  */
546     movi    a3, pxCurrentTCB            /* cp_state =                       */
547     l32i    a3, a3, 0
548     l32i    a2, a3, CP_TOPOFSTACK_OFFS
549
550     rsr     a3, CPENABLE
551     s16i     a3, a2, XT_CPENABLE         /* cp_state->cpenable = CPENABLE;   */
552     movi    a3, 0
553     wsr     a3, CPENABLE                /* disable all co-processors        */
554     #endif
555
556     #ifdef __XTENSA_CALL0_ABI__
557     /* Tail-call dispatcher. */
558     call0   _frxt_dispatch
559     /* Never reaches here. */
560     #else
561     RET(16)
562     #endif
563
564 /*
565 **********************************************************************************************************
566 *                                        _frxt_task_coproc_state
567 *                                   void _frxt_task_coproc_state(void)
568 *
569 * Implements the Xtensa RTOS porting layer's XT_RTOS_CP_STATE function for FreeRTOS.
570 *
571 * May only be called when a task is running, not within an interrupt handler (returns 0 in that case).
572 * May only be called from assembly code by the 'call0' instruction. Does NOT obey ABI conventions.
573 * Returns in A15 a pointer to the base of the co-processor state save area for the current task.
574 * See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
575 *
576 **********************************************************************************************************
577 */
578 #if XCHAL_CP_NUM > 0
579
580     .globl  _frxt_task_coproc_state
581     .type   _frxt_task_coproc_state,@function
582     .align  4
583 _frxt_task_coproc_state:
584
585     movi    a15, port_xSchedulerRunning /* if (port_xSchedulerRunning              */
586     l32i    a15, a15, 0
587     beqz    a15, 1f
588     movi    a15, port_interruptNesting  /* && port_interruptNesting == 0           */
589     l32i    a15, a15, 0
590     bnez    a15, 1f
591     movi    a15, pxCurrentTCB
592     l32i    a15, a15, 0                 /* && pxCurrentTCB != 0) {                 */
593     beqz    a15, 2f
594     l32i    a15, a15, CP_TOPOFSTACK_OFFS
595     ret
596
597 1:  movi    a15, 0
598 2:  ret
599
600 #endif /* XCHAL_CP_NUM > 0 */