2 //-----------------------------------------------------------------------------
\r
3 // Copyright (c) 2003-2015 Cadence Design Systems, Inc.
\r
5 // Permission is hereby granted, free of charge, to any person obtaining
\r
6 // a copy of this software and associated documentation files (the
\r
7 // "Software"), to deal in the Software without restriction, including
\r
8 // without limitation the rights to use, copy, modify, merge, publish,
\r
9 // distribute, sublicense, and/or sell copies of the Software, and to
\r
10 // permit persons to whom the Software is furnished to do so, subject to
\r
11 // the following conditions:
\r
13 // The above copyright notice and this permission notice shall be included
\r
14 // in all copies or substantial portions of the Software.
\r
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
\r
19 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
\r
20 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
\r
21 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
\r
22 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
23 //-----------------------------------------------------------------------------
\r
26 #include "xtensa_rtos.h"
\r
28 #define TOPOFSTACK_OFFS 0x00 /* StackType_t *pxTopOfStack */
\r
29 #define CP_TOPOFSTACK_OFFS 0x04 /* xMPU_SETTINGS.coproc_area */
\r
31 .extern pxCurrentTCB
\r
35 *******************************************************************************
\r
36 * Interrupt stack. The size of the interrupt stack is determined by the config
\r
37 * parameter "configISR_STACK_SIZE" in FreeRTOSConfig.h
\r
38 *******************************************************************************
\r
42 .global port_IntStack
\r
44 .space configISR_STACK_SIZE
\r
52 *******************************************************************************
\r
53 * _frxt_setup_switch
\r
54 * void _frxt_setup_switch(void);
\r
56 * Sets an internal flag indicating that a task switch is required on return
\r
57 * from interrupt handling.
\r
59 *******************************************************************************
\r
61 .global _frxt_setup_switch
\r
62 .type _frxt_setup_switch,@function
\r
68 movi a2, port_switch_flag
\r
75 *******************************************************************************
\r
77 * void _frxt_int_enter(void)
\r
79 * Implements the Xtensa RTOS porting layer's XT_RTOS_INT_ENTER function for
\r
80 * freeRTOS. Saves the rest of the interrupt context (not already saved).
\r
81 * May only be called from assembly code by the 'call0' instruction, with
\r
82 * interrupts disabled.
\r
83 * See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
\r
85 *******************************************************************************
\r
87 .globl _frxt_int_enter
\r
88 .type _frxt_int_enter,@function
\r
92 /* Save a12-13 in the stack frame as required by _xt_context_save. */
\r
93 s32i a12, a1, XT_STK_A12
\r
94 s32i a13, a1, XT_STK_A13
\r
96 /* Save return address in a safe place (free a0). */
\r
99 /* Save the rest of the interrupted context (preserves A12-13). */
\r
100 call0 _xt_context_save
\r
103 Save interrupted task's SP in TCB only if not nesting.
\r
104 Manage nesting directly rather than call the generic IntEnter()
\r
105 (in windowed ABI we can't call a C function here anyway because PS.EXCM is still set).
\r
107 movi a2, port_xSchedulerRunning
\r
108 movi a3, port_interruptNesting
\r
109 l32i a2, a2, 0 /* a2 = port_xSchedulerRunning */
\r
110 beqz a2, 1f /* scheduler not running, no tasks */
\r
111 l32i a2, a3, 0 /* a2 = port_interruptNesting */
\r
112 addi a2, a2, 1 /* increment nesting count */
\r
113 s32i a2, a3, 0 /* save nesting count */
\r
114 bnei a2, 1, .Lnested /* !=0 before incr, so nested */
\r
116 movi a2, pxCurrentTCB
\r
117 l32i a2, a2, 0 /* a2 = current TCB */
\r
119 s32i a1, a2, TOPOFSTACK_OFFS /* pxCurrentTCB->pxTopOfStack = SP */
\r
120 movi a1, port_IntStackTop /* a1 = top of intr stack */
\r
124 mov a0, a12 /* restore return addr and return */
\r
128 *******************************************************************************
\r
130 * void _frxt_int_exit(void)
\r
132 * Implements the Xtensa RTOS porting layer's XT_RTOS_INT_EXIT function for
\r
133 * FreeRTOS. If required, calls vPortYieldFromInt() to perform task context
\r
134 * switching, restore the (possibly) new task's context, and return to the
\r
135 * exit dispatcher saved in the task's stack frame at XT_STK_EXIT.
\r
136 * May only be called from assembly code by the 'call0' instruction. Does not
\r
137 * return to caller.
\r
138 * See the description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
\r
140 *******************************************************************************
\r
142 .globl _frxt_int_exit
\r
143 .type _frxt_int_exit,@function
\r
147 movi a2, port_xSchedulerRunning
\r
148 movi a3, port_interruptNesting
\r
149 rsil a0, XCHAL_EXCM_LEVEL /* lock out interrupts */
\r
150 l32i a2, a2, 0 /* a2 = port_xSchedulerRunning */
\r
151 beqz a2, .Lnoswitch /* scheduler not running, no tasks */
\r
152 l32i a2, a3, 0 /* a2 = port_interruptNesting */
\r
153 addi a2, a2, -1 /* decrement nesting count */
\r
154 s32i a2, a3, 0 /* save nesting count */
\r
155 bnez a2, .Lnesting /* !=0 after decr so still nested */
\r
157 movi a2, pxCurrentTCB
\r
158 l32i a2, a2, 0 /* a2 = current TCB */
\r
159 beqz a2, 1f /* no task ? go to dispatcher */
\r
160 l32i a1, a2, TOPOFSTACK_OFFS /* SP = pxCurrentTCB->pxTopOfStack */
\r
162 movi a2, port_switch_flag /* address of switch flag */
\r
163 l32i a3, a2, 0 /* a3 = port_switch_flag */
\r
164 beqz a3, .Lnoswitch /* flag = 0 means no switch reqd */
\r
166 s32i a3, a2, 0 /* zero out the flag for next time */
\r
170 Call0 ABI callee-saved regs a12-15 need to be saved before possible preemption.
\r
171 However a12-13 were already saved by _frxt_int_enter().
\r
173 #ifdef __XTENSA_CALL0_ABI__
\r
174 s32i a14, a1, XT_STK_A14
\r
175 s32i a15, a1, XT_STK_A15
\r
178 #ifdef __XTENSA_CALL0_ABI__
\r
179 call0 vPortYieldFromInt /* call dispatch inside the function; never returns */
\r
181 call4 vPortYieldFromInt /* this one returns */
\r
182 call0 _frxt_dispatch /* tail-call dispatcher */
\r
183 /* Never returns here. */
\r
188 If we came here then about to resume the interrupted task.
\r
193 We come here only if there was no context switch, that is if this
\r
194 is a nested interrupt, or the interrupted task was not preempted.
\r
195 In either case there's no need to load the SP.
\r
198 /* Restore full context from interrupt stack frame */
\r
199 call0 _xt_context_restore
\r
202 Must return via the exit dispatcher corresponding to the entrypoint from which
\r
203 this was called. Interruptee's A0, A1, PS, PC are restored and the interrupt
\r
204 stack frame is deallocated in the exit dispatcher.
\r
206 l32i a0, a1, XT_STK_EXIT
\r
211 **********************************************************************************************************
\r
213 * void _frxt_timer_int(void)
\r
215 * Implements the Xtensa RTOS porting layer's XT_RTOS_TIMER_INT function for FreeRTOS.
\r
216 * Called every timer interrupt.
\r
217 * Manages the tick timer and calls xPortSysTickHandler() every tick.
\r
218 * See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
\r
220 * Callable from C (obeys ABI conventions). Implemented in assmebly code for performance.
\r
222 **********************************************************************************************************
\r
224 .globl _frxt_timer_int
\r
225 .type _frxt_timer_int,@function
\r
230 Xtensa timers work by comparing a cycle counter with a preset value. Once the match occurs
\r
231 an interrupt is generated, and the handler has to set a new cycle count into the comparator.
\r
232 To avoid clock drift due to interrupt latency, the new cycle count is computed from the old,
\r
233 not the time the interrupt was serviced. However if a timer interrupt is ever serviced more
\r
234 than one tick late, it is necessary to process multiple ticks until the new cycle count is
\r
235 in the future, otherwise the next timer interrupt would not occur until after the cycle
\r
236 counter had wrapped (2^32 cycles later).
\r
240 old_ccompare = read_ccompare_i();
\r
241 write_ccompare_i( old_ccompare + divisor );
\r
243 diff = read_ccount() - old_ccompare;
\r
244 } while ( diff > divisor );
\r
249 .L_xt_timer_int_catchup:
\r
251 /* Update the timer comparator for the next tick. */
\r
252 #ifdef XT_CLOCK_FREQ
\r
253 movi a2, XT_TICK_DIVISOR /* a2 = comparator increment */
\r
255 movi a3, _xt_tick_divisor
\r
256 l32i a2, a3, 0 /* a2 = comparator increment */
\r
258 rsr a3, XT_CCOMPARE /* a3 = old comparator value */
\r
259 add a4, a3, a2 /* a4 = new comparator value */
\r
260 wsr a4, XT_CCOMPARE /* update comp. and clear interrupt */
\r
263 #ifdef __XTENSA_CALL0_ABI__
\r
264 /* Preserve a2 and a3 across C calls. */
\r
269 /* Call the FreeRTOS tick handler (see port.c). */
\r
270 #ifdef __XTENSA_CALL0_ABI__
\r
271 call0 xPortSysTickHandler
\r
273 call4 xPortSysTickHandler
\r
276 #ifdef __XTENSA_CALL0_ABI__
\r
277 /* Restore a2 and a3. */
\r
282 /* Check if we need to process more ticks to catch up. */
\r
283 esync /* ensure comparator update complete */
\r
284 rsr a4, CCOUNT /* a4 = cycle count */
\r
285 sub a4, a4, a3 /* diff = ccount - old comparator */
\r
286 blt a2, a4, .L_xt_timer_int_catchup /* repeat while diff > divisor */
\r
291 **********************************************************************************************************
\r
292 * _frxt_tick_timer_init
\r
293 * void _frxt_tick_timer_init(void)
\r
295 * Initialize timer and timer interrrupt handler (_xt_tick_divisor_init() has already been been called).
\r
296 * Callable from C (obeys ABI conventions on entry).
\r
298 **********************************************************************************************************
\r
300 .globl _frxt_tick_timer_init
\r
301 .type _frxt_tick_timer_init,@function
\r
303 _frxt_tick_timer_init:
\r
307 /* Set up the periodic tick timer (assume enough time to complete init). */
\r
308 #ifdef XT_CLOCK_FREQ
\r
309 movi a3, XT_TICK_DIVISOR
\r
311 movi a2, _xt_tick_divisor
\r
314 rsr a2, CCOUNT /* current cycle count */
\r
315 add a2, a2, a3 /* time of first timer interrupt */
\r
316 wsr a2, XT_CCOMPARE /* set the comparator */
\r
319 Enable the timer interrupt at the device level. Don't write directly
\r
320 to the INTENABLE register because it may be virtualized.
\r
322 #ifdef __XTENSA_CALL0_ABI__
\r
323 movi a2, XT_TIMER_INTEN
\r
326 movi a6, XT_TIMER_INTEN
\r
333 **********************************************************************************************************
\r
334 * DISPATCH THE HIGH READY TASK
\r
335 * void _frxt_dispatch(void)
\r
337 * Switch context to the highest priority ready task, restore its state and dispatch control to it.
\r
339 * This is a common dispatcher that acts as a shared exit path for all the context switch functions
\r
340 * including vPortYield() and vPortYieldFromInt(), all of which tail-call this dispatcher
\r
341 * (for windowed ABI vPortYieldFromInt() calls it indirectly via _frxt_int_exit() ).
\r
343 * The Xtensa port uses different stack frames for solicited and unsolicited task suspension (see
\r
344 * comments on stack frames in xtensa_context.h). This function restores the state accordingly.
\r
345 * If restoring a task that solicited entry, restores the minimal state and leaves CPENABLE clear.
\r
346 * If restoring a task that was preempted, restores all state including the task's CPENABLE.
\r
349 * pxCurrentTCB points to the TCB of the task to suspend,
\r
350 * Because it is tail-called without a true function entrypoint, it needs no 'entry' instruction.
\r
353 * If incoming task called vPortYield() (solicited), this function returns as if from vPortYield().
\r
354 * If incoming task was preempted by an interrupt, this function jumps to exit dispatcher.
\r
356 **********************************************************************************************************
\r
358 .globl _frxt_dispatch
\r
359 .type _frxt_dispatch,@function
\r
363 #ifdef __XTENSA_CALL0_ABI__
\r
364 call0 vTaskSwitchContext // Get next TCB to resume
\r
365 movi a2, pxCurrentTCB
\r
367 movi a2, pxCurrentTCB
\r
368 call4 vTaskSwitchContext // Get next TCB to resume
\r
371 l32i sp, a3, TOPOFSTACK_OFFS /* SP = next_TCB->pxTopOfStack; */
\r
374 /* Determine the type of stack frame. */
\r
375 l32i a2, sp, XT_STK_EXIT /* exit dispatcher or solicited flag */
\r
376 bnez a2, .L_frxt_dispatch_stk
\r
378 .L_frxt_dispatch_sol:
\r
380 /* Solicited stack frame. Restore minimal context and return from vPortYield(). */
\r
381 l32i a3, sp, XT_SOL_PS
\r
382 #ifdef __XTENSA_CALL0_ABI__
\r
383 l32i a12, sp, XT_SOL_A12
\r
384 l32i a13, sp, XT_SOL_A13
\r
385 l32i a14, sp, XT_SOL_A14
\r
386 l32i a15, sp, XT_SOL_A15
\r
388 l32i a0, sp, XT_SOL_PC
\r
389 #if XCHAL_CP_NUM > 0
\r
390 /* Ensure wsr.CPENABLE is complete (should be, it was cleared on entry). */
\r
393 /* As soons as PS is restored, interrupts can happen. No need to sync PS. */
\r
395 #ifdef __XTENSA_CALL0_ABI__
\r
396 addi sp, sp, XT_SOL_FRMSZ
\r
402 .L_frxt_dispatch_stk:
\r
404 #if XCHAL_CP_NUM > 0
\r
405 /* Restore CPENABLE from task's co-processor save area. */
\r
406 movi a3, pxCurrentTCB /* cp_state = */
\r
408 l32i a2, a3, CP_TOPOFSTACK_OFFS /* StackType_t *pxStack; */
\r
409 l16ui a3, a2, XT_CPENABLE /* CPENABLE = cp_state->cpenable; */
\r
413 /* Interrupt stack frame. Restore full context and return to exit dispatcher. */
\r
414 call0 _xt_context_restore
\r
416 /* In Call0 ABI, restore callee-saved regs (A12, A13 already restored). */
\r
417 #ifdef __XTENSA_CALL0_ABI__
\r
418 l32i a14, sp, XT_STK_A14
\r
419 l32i a15, sp, XT_STK_A15
\r
422 #if XCHAL_CP_NUM > 0
\r
423 /* Ensure wsr.CPENABLE has completed. */
\r
428 Must return via the exit dispatcher corresponding to the entrypoint from which
\r
429 this was called. Interruptee's A0, A1, PS, PC are restored and the interrupt
\r
430 stack frame is deallocated in the exit dispatcher.
\r
432 l32i a0, sp, XT_STK_EXIT
\r
437 **********************************************************************************************************
\r
438 * PERFORM A SOLICTED CONTEXT SWITCH (from a task)
\r
439 * void vPortYield(void)
\r
441 * This function saves the minimal state needed for a solicited task suspension, clears CPENABLE,
\r
442 * then tail-calls the dispatcher _frxt_dispatch() to perform the actual context switch
\r
445 * pxCurrentTCB points to the TCB of the task to suspend
\r
446 * Callable from C (obeys ABI conventions on entry).
\r
448 * Does not return to caller.
\r
450 **********************************************************************************************************
\r
453 .type vPortYield,@function
\r
457 #ifdef __XTENSA_CALL0_ABI__
\r
458 addi sp, sp, -XT_SOL_FRMSZ
\r
460 entry sp, XT_SOL_FRMSZ
\r
464 s32i a0, sp, XT_SOL_PC
\r
465 s32i a2, sp, XT_SOL_PS
\r
466 #ifdef __XTENSA_CALL0_ABI__
\r
467 s32i a12, sp, XT_SOL_A12 /* save callee-saved registers */
\r
468 s32i a13, sp, XT_SOL_A13
\r
469 s32i a14, sp, XT_SOL_A14
\r
470 s32i a15, sp, XT_SOL_A15
\r
472 /* Spill register windows. Calling xthal_window_spill() causes extra */
\r
473 /* spills and reloads, so we will set things up to call the _nw version */
\r
474 /* instead to save cycles. */
\r
475 movi a6, ~(PS_WOE_MASK|PS_INTLEVEL_MASK) /* spills a4-a7 if needed */
\r
476 and a2, a2, a6 /* clear WOE, INTLEVEL */
\r
477 addi a2, a2, XCHAL_EXCM_LEVEL /* set INTLEVEL */
\r
480 call0 xthal_window_spill_nw
\r
481 l32i a2, sp, XT_SOL_PS /* restore PS */
\r
485 rsil a2, XCHAL_EXCM_LEVEL /* disable low/med interrupts */
\r
487 #if XCHAL_CP_NUM > 0
\r
488 /* Save coprocessor callee-saved state (if any). At this point CPENABLE */
\r
489 /* should still reflect which CPs were in use (enabled). */
\r
490 call0 _xt_coproc_savecs
\r
493 movi a2, pxCurrentTCB
\r
495 l32i a2, a2, 0 /* a2 = pxCurrentTCB */
\r
496 s32i a3, sp, XT_SOL_EXIT /* 0 to flag as solicited frame */
\r
497 s32i sp, a2, TOPOFSTACK_OFFS /* pxCurrentTCB->pxTopOfStack = SP */
\r
499 #if XCHAL_CP_NUM > 0
\r
500 /* Clear CPENABLE, also in task's co-processor state save area. */
\r
501 l32i a2, a2, CP_TOPOFSTACK_OFFS /* a2 = pxCurrentTCB->cp_state */
\r
505 s16i a3, a2, XT_CPENABLE /* clear saved cpenable */
\r
509 /* Tail-call dispatcher. */
\r
510 call0 _frxt_dispatch
\r
511 /* Never reaches here. */
\r
515 **********************************************************************************************************
\r
516 * PERFORM AN UNSOLICITED CONTEXT SWITCH (from an interrupt)
\r
517 * void vPortYieldFromInt(void)
\r
519 * This calls the context switch hook (removed), saves and clears CPENABLE, then tail-calls the dispatcher
\r
520 * _frxt_dispatch() to perform the actual context switch.
\r
523 * Interrupted task context has been saved in an interrupt stack frame at pxCurrentTCB->pxTopOfStack.
\r
524 * pxCurrentTCB points to the TCB of the task to suspend,
\r
525 * Callable from C (obeys ABI conventions on entry).
\r
528 * Windowed ABI defers the actual context switch until the stack is unwound to interrupt entry.
\r
529 * Call0 ABI tail-calls the dispatcher directly (no need to unwind) so does not return to caller.
\r
531 **********************************************************************************************************
\r
533 .globl vPortYieldFromInt
\r
534 .type vPortYieldFromInt,@function
\r
540 #if XCHAL_CP_NUM > 0
\r
541 /* Save CPENABLE in task's co-processor save area, and clear CPENABLE. */
\r
542 movi a3, pxCurrentTCB /* cp_state = */
\r
544 l32i a2, a3, CP_TOPOFSTACK_OFFS
\r
547 s16i a3, a2, XT_CPENABLE /* cp_state->cpenable = CPENABLE; */
\r
549 wsr a3, CPENABLE /* disable all co-processors */
\r
552 #ifdef __XTENSA_CALL0_ABI__
\r
553 /* Tail-call dispatcher. */
\r
554 call0 _frxt_dispatch
\r
555 /* Never reaches here. */
\r
561 **********************************************************************************************************
\r
562 * _frxt_task_coproc_state
\r
563 * void _frxt_task_coproc_state(void)
\r
565 * Implements the Xtensa RTOS porting layer's XT_RTOS_CP_STATE function for FreeRTOS.
\r
567 * May only be called when a task is running, not within an interrupt handler (returns 0 in that case).
\r
568 * May only be called from assembly code by the 'call0' instruction. Does NOT obey ABI conventions.
\r
569 * Returns in A15 a pointer to the base of the co-processor state save area for the current task.
\r
570 * See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h.
\r
572 **********************************************************************************************************
\r
574 #if XCHAL_CP_NUM > 0
\r
576 .globl _frxt_task_coproc_state
\r
577 .type _frxt_task_coproc_state,@function
\r
579 _frxt_task_coproc_state:
\r
581 movi a15, port_xSchedulerRunning /* if (port_xSchedulerRunning */
\r
584 movi a15, port_interruptNesting /* && port_interruptNesting == 0 */
\r
587 movi a15, pxCurrentTCB
\r
588 l32i a15, a15, 0 /* && pxCurrentTCB != 0) { */
\r
590 l32i a15, a15, CP_TOPOFSTACK_OFFS
\r
596 #endif /* XCHAL_CP_NUM > 0 */
\r