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