zephyr/arch/sparc/core/interrupt_trap.S
Martin Åberg 53a4acb2dc SPARC: add FPU support
This change adds full shared floating point support for the SPARC
architecture.

All SPARC floating point registers are scratch registers with respect
to function call boundaries. That means we only have to save floating
point registers when switching threads in ISR. The registers are
stored to the corresponding thread stack.

FPU is disabled when calling ISR. Any attempt to use FPU in ISR
will generate the fp_disabled trap which causes Zephyr fatal error.

- This commit adds no new thread state.
- All FPU contest save/restore is synchronous and lazy FPU context
  switch is not implemented.

Signed-off-by: Martin Åberg <martin.aberg@gaisler.com>
2020-12-04 14:33:43 +02:00

331 lines
7.6 KiB
ArmAsm

/*
* Copyright (c) 2019-2020 Cobham Gaisler AB
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <toolchain.h>
#include <linker/sections.h>
#include <offsets_short.h>
#include <arch/sparc/sparc.h>
#include "stack_offsets.h"
GTEXT(__sparc_trap_interrupt)
GTEXT(__sparc_trap_irq_offload)
/*
* Interrupt trap handler
*
* - IU state is saved and restored
*
* On entry:
* %l0: psr (set by trap code)
* %l1: pc
* %l2: npc
* %l3: SPARC interrupt request level (bp_IRL)
* %fp: %sp of current register window at trap time
*
* This module also implements the IRQ offload support. The handling is the
* same as for asynchronous maskable interrupts, with the following exceptions:
* - Do not re-execute the causing (ta) instruction at trap exit.
* - A dedicated interrupt request level (0x8d) is used.
* - z_sparc_enter_irq() knows how to interpret this interrupt request level.
*/
SECTION_SUBSEC_FUNC(TEXT, __sparc_trap_interrupt, __sparc_trap_irq_offload)
/* Preparation in the case of synchronous IRQ offload. */
mov %l2, %l1
add %l2, 4, %l2
set 0x8d, %l3
__sparc_trap_interrupt:
/* %g2, %g3 are used at manual window overflow so save temporarily */
mov %g2, %l4
mov %g3, %l5
/* We may have trapped into the invalid window. If so, make it valid. */
rd %wim, %g2
srl %g2, %l0, %g3
cmp %g3, 1
bne .Lwodone
nop
/* Do the window overflow. */
sll %g2, (CONFIG_SPARC_NWIN-1), %g3
srl %g2, 1, %g2
or %g2, %g3, %g2
/* Enter window to save. */
save
/* Install new wim calculated above. */
mov %g2, %wim
nop
nop
nop
/* Put registers on the dedicated save area of the ABI stack frame. */
std %l0, [%sp + 0x00]
std %l2, [%sp + 0x08]
std %l4, [%sp + 0x10]
std %l6, [%sp + 0x18]
std %i0, [%sp + 0x20]
std %i2, [%sp + 0x28]
std %i4, [%sp + 0x30]
std %i6, [%sp + 0x38]
/* Leave saved window. */
restore
.Lwodone:
/*
* %l4: %g2 at trap time
* %l5: %g3 at trap time
*
* Save the state of the interrupted task including global registers on
* the task stack.
*
* IMPORTANT: Globals are saved here as well on the task stack, since a
* context switch might happen before the context of this interrupted
* task is restored.
*/
/* Allocate stack for isr context. */
sub %fp, ISF_SIZE, %sp
/*
* %fp: %sp of interrupted task
* %sp: %sp of interrupted task - ISF_SIZE.
* (fits what we store here)
*
* Save the interrupted context.
*/
std %l0, [%sp + ISF_PSR_OFFSET] /* psr pc */
st %l2, [%sp + ISF_NPC_OFFSET] /* npc */
st %g1, [%sp + ISF_G1_OFFSET] /* g1 */
std %l4, [%sp + ISF_G2_OFFSET] /* g2 g3 */
st %g4, [%sp + ISF_G4_OFFSET] /* g4 */
rd %y, %g1
st %g1, [%sp + ISF_Y_OFFSET] /* y */
/* %l5: reference to _kernel */
set _kernel, %l5
/* Switch to interrupt stack. */
mov %sp, %fp
ld [%l5 + _kernel_offset_to_irq_stack], %sp
/* Allocate a full C stack frame */
sub %sp, STACK_FRAME_SIZE, %sp
/*
* %fp: %sp of interrupted task - ISF_SIZE.
* %sp: irq stack - 96. An ABI frame
*/
/* Enable traps, raise PIL to mask all maskable interrupts. */
or %l0, PSR_PIL, %l6
#if defined(CONFIG_FPU)
/*
* We now check if the interrupted context was using the FPU. The
* result is stored in register l5 which will either get the value 0
* (FPU not used) or PSR_EF (FPU used).
*
* If the FPU was used by the interrupted context, then we do two
* things:
* 1. Store FSR to memory. This has the side-effect of completing all
* pending FPU operations.
* 2. Disable FPU. Floating point instructions in the ISR will trap.
*
* The FPU is be enabled again if needed after the ISR has returned.
*/
set PSR_EF, %l5
andcc %l0, %l5, %l5
bne,a 1f
st %fsr, [%sp]
1:
andn %l6, %l5, %l6
#endif
wr %l6, PSR_ET, %psr
nop
nop
nop
#ifdef CONFIG_TRACING_ISR
call sys_trace_isr_enter
nop
#endif
/* SPARC interrupt request level is the first argument */
call z_sparc_enter_irq
mov %l3, %o0
#ifdef CONFIG_TRACING_ISR
call sys_trace_isr_exit
nop
#endif
/*
* %fp: %sp of interrupted task - ISF_SIZE.
* %sp: irq stack - 96. An ABI frame
*/
#ifdef CONFIG_PREEMPT_ENABLED
/* allocate stack for calling C function and for its output value */
sub %fp, (96+8), %sp
/*
* %fp: %sp of interrupted task - ISF_SIZE.
* %sp: %sp of interrupted task - ISF_SIZE - STACK_FRAME_SIZE - 8.
*/
call z_arch_get_next_switch_handle
add %sp, 96, %o0
/* we get old thread as "return value" on stack */
ld [%sp + 96], %o1
/*
* o0: new thread
* o1: old thread
*/
cmp %o0, %o1
beq .Lno_reschedule
/* z_sparc_context_switch() is a leaf function not using stack. */
add %sp, (96+8-64), %sp
#if defined(CONFIG_FPU_SHARING)
/* IF PSR_EF at trap time then store the FP context. */
cmp %l5, 0
be .Lno_fp_context
nop
/*
* PSR_EF was 1 at trap time so save the FP registers on stack.
* - Set PSR_EF so we can access the FP registers.
* - Allocate space for the FP registers above the save area used for
* the z_sparc_context_switch() call.
*/
wr %l6, %l5, %psr
nop
nop
nop
sub %sp, 34 * 4, %sp
std %f0, [%sp + 64 + 0x00]
std %f2, [%sp + 64 + 0x08]
std %f4, [%sp + 64 + 0x10]
std %f6, [%sp + 64 + 0x18]
std %f8, [%sp + 64 + 0x20]
std %f10, [%sp + 64 + 0x28]
std %f12, [%sp + 64 + 0x30]
std %f14, [%sp + 64 + 0x38]
std %f16, [%sp + 64 + 0x40]
std %f18, [%sp + 64 + 0x48]
std %f20, [%sp + 64 + 0x50]
std %f22, [%sp + 64 + 0x58]
std %f24, [%sp + 64 + 0x60]
std %f26, [%sp + 64 + 0x68]
std %f28, [%sp + 64 + 0x70]
std %f30, [%sp + 64 + 0x78]
call z_sparc_context_switch
st %fsr, [%sp + 64 + 0x80]
ldd [%sp + 64 + 0x00], %f0
ldd [%sp + 64 + 0x08], %f2
ldd [%sp + 64 + 0x10], %f4
ldd [%sp + 64 + 0x18], %f6
ldd [%sp + 64 + 0x20], %f8
ldd [%sp + 64 + 0x28], %f10
ldd [%sp + 64 + 0x30], %f12
ldd [%sp + 64 + 0x38], %f14
ldd [%sp + 64 + 0x40], %f16
ldd [%sp + 64 + 0x48], %f18
ldd [%sp + 64 + 0x50], %f20
ldd [%sp + 64 + 0x58], %f22
ldd [%sp + 64 + 0x60], %f24
ldd [%sp + 64 + 0x68], %f26
ldd [%sp + 64 + 0x70], %f28
ldd [%sp + 64 + 0x78], %f30
ld [%sp + 64 + 0x80], %fsr
ba .Lno_reschedule
add %sp, 34 * 4, %sp
.Lno_fp_context:
#endif /* CONFIG_FPU_SHARING */
call z_sparc_context_switch
nop
.Lno_reschedule:
#endif /* CONFIG_PREEMPT_ENABLED */
/* Restore the interrupted context. */
ld [%fp + ISF_Y_OFFSET], %g1
wr %g1, 0, %y
ldd [%fp + ISF_PSR_OFFSET], %l0 /* psr, pc */
ld [%fp + ISF_NPC_OFFSET], %l2 /* npc */
/* NOTE: %g1 will be restored later */
/* %g1 is used to access the stack frame later */
mov %fp, %g1
ldd [%fp + ISF_G2_OFFSET], %g2
ld [%fp + ISF_G4_OFFSET], %g4
add %fp, ISF_SIZE, %fp
/*
* Install the PSR we got from the interrupt context. Current PSR.CWP
* is preserved. Keep PSR.ET=0 until we do "rett".
*/
rd %psr, %l3
and %l3, PSR_CWP, %l3
andn %l0, (PSR_CWP | PSR_ET), %l0
or %l3, %l0, %l0
mov %l0, %psr
nop
nop
nop
/* Calculate %l6 := (cwp+1) % NWIN */
rd %wim, %l3
set (CONFIG_SPARC_NWIN), %l7
add %l0, 1, %l6
and %l6, PSR_CWP, %l6
cmp %l6, %l7
bge,a .Lwrapok
mov 0, %l6
.Lwrapok:
/* Determine if we must prepare the return window. */
/* %l5 := %wim >> (cwp+1) */
srl %l3, %l6, %l5
/* %l5 is 1 if (cwp+1) is an invalid window */
cmp %l5, 1
bne .Lwudone
sub %l7, 1, %l7 /* %l7 := NWIN - 1 */
/* Do the window underflow. */
sll %l3, 1, %l4
srl %l3, %l7, %l5
wr %l4, %l5, %wim
nop
nop
nop
restore
ldd [%g1 + 0x00], %l0
ldd [%g1 + 0x08], %l2
ldd [%g1 + 0x10], %l4
ldd [%g1 + 0x18], %l6
ldd [%g1 + 0x20], %i0
ldd [%g1 + 0x28], %i2
ldd [%g1 + 0x30], %i4
ldd [%g1 + 0x38], %i6
save
.Lwudone:
/*
* Restore %psr since we may have trashed condition codes. PSR.ET is
* still 0.
*/
wr %l0, %psr
nop
nop
nop
/* restore g1 */
ld [%g1 + ISF_G1_OFFSET], %g1
jmp %l1
rett %l2