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>
This commit is contained in:
Martin Åberg 2020-11-30 12:19:27 +01:00 committed by Ioannis Glaropoulos
commit 53a4acb2dc
5 changed files with 145 additions and 7 deletions

View file

@ -730,7 +730,7 @@ menu "Floating Point Options"
config FPU
bool "Enable floating point unit (FPU)"
depends on CPU_HAS_FPU
depends on ARC || ARM || RISCV || X86
depends on ARC || ARM || RISCV || SPARC || X86
help
This option enables the hardware Floating Point Unit (FPU), in order to
support using the floating point registers and instructions.

View file

@ -117,8 +117,30 @@ __sparc_trap_interrupt:
*/
/* Enable traps, raise PIL to mask all maskable interrupts. */
or %l0, PSR_PIL, %o0
wr %o0, PSR_ET, %psr
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
@ -159,7 +181,69 @@ __sparc_trap_interrupt:
*/
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:

View file

@ -27,6 +27,12 @@ struct init_stack_frame {
uint32_t pad[8];
};
#if defined(CONFIG_FPU_SHARING)
#define USER_FP_MASK K_FP_REGS
#else
#define USER_FP_MASK 0
#endif
void arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack,
char *stack_ptr, k_thread_entry_t entry,
void *p1, void *p2, void *p3)
@ -47,6 +53,16 @@ void arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack,
thread->callee_saved.o7 = (uint32_t) z_thread_entry_wrapper - 8;
thread->callee_saved.psr = PSR_S | PSR_PS | PSR_ET;
if (IS_ENABLED(CONFIG_FPU_SHARING)) {
/* Selected threads can use the FPU */
if (thread->base.user_options & USER_FP_MASK) {
thread->callee_saved.psr |= PSR_EF;
}
} else if (IS_ENABLED(CONFIG_FPU)) {
/* Any thread can use the FPU */
thread->callee_saved.psr |= PSR_EF;
}
thread->switch_handle = thread;
}
@ -56,3 +72,10 @@ void *z_arch_get_next_switch_handle(struct k_thread **old_thread)
return z_get_next_switch_handle(*old_thread);
}
#if defined(CONFIG_FPU_SHARING)
int arch_float_disable(struct k_thread *thread)
{
return -ENOSYS;
}
#endif /* CONFIG_FPU_SHARING */

View file

@ -1,7 +1,9 @@
# SPDX-License-Identifier: Apache-2.0
list(APPEND TOOLCHAIN_C_FLAGS -msoft-float)
list(APPEND TOOLCHAIN_LD_FLAGS -msoft-float)
if(NOT CONFIG_FPU)
list(APPEND TOOLCHAIN_C_FLAGS -msoft-float)
list(APPEND TOOLCHAIN_LD_FLAGS -msoft-float)
endif()
if(CONFIG_SPARC_CASA)
# SPARC V8, mul/div, casa

View file

@ -9,8 +9,9 @@ configurations that support these registers.
.. note::
Floating point services are currently available only for boards
based on ARM Cortex-M SoCs supporting the Floating Point Extension,
the Intel x86 architecture and ARCv2 SoCs supporting the Floating
Point Extension. The services provided are architecture specific.
the Intel x86 architecture, the SPARC architecture and ARCv2 SoCs
supporting the Floating Point Extension. The services provided
are architecture specific.
The kernel does not support the use of floating point registers by ISRs.
@ -176,6 +177,34 @@ FP context of threads that are not using the FP registers. An extra 84 bytes
(single floating point hardware) or 164 bytes (double floating point hardware)
of stack space is required to load and store floating point registers.
SPARC architecture
------------------
On the SPARC architecture, the kernel treats each thread as a non-user
or FPU user and the thread must be tagged by one of the
following techniques:
* A statically-created thread can be tagged by passing the
:c:macro:`K_FP_REGS` option to :c:macro:`K_THREAD_DEFINE`.
* A dynamically-created thread can be tagged by passing the
:c:macro:`K_FP_REGS` to :c:func:`k_thread_create`.
During thread context switch at exit from interrupt handler, the SPARC
kernel saves *all* floating point registers, if the FPU was enabled in
the switched-out thread. Floating point registers are saved on the thread's
stack. Floating point registers are restored when a thread context is restored
iff they were saved at the context save. Saving and restoring of the floating
point registers is synchronous and thus not lazy. The FPU is always disabled
when an ISR is called (independent of :option:`CONFIG_FPU_SHARING`).
Floating point disabling with :c:func:`k_float_disable` is not implemented.
When :option:`CONFIG_FPU_SHARING` is used, then 136 bytes of stack space
is required for each FPU user thread to load and store floating point
registers. No extra stack is required if :option:`CONFIG_FPU_SHARING` is
not used.
x86 architecture
----------------