You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2023/06/19 11:28:19 UTC
[nuttx] 06/06: riscv/lazyfpu: Add option to disable lazy FPU
This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 425cc8998991b129b7ed12f97c56f606fd20b9aa
Author: Ville Juven <vi...@unikie.com>
AuthorDate: Wed Jun 7 14:02:48 2023 +0300
riscv/lazyfpu: Add option to disable lazy FPU
Adds option to use the old implementation where FPU is stored into
the process stack.
---
arch/Kconfig | 35 +++++++++++++++++++++++++++++++++
arch/risc-v/Kconfig | 5 +++++
arch/risc-v/include/irq.h | 8 +++++++-
arch/risc-v/src/common/riscv_fpu.S | 4 ++++
arch/risc-v/src/common/riscv_internal.h | 21 ++++++++++++++++++--
arch/risc-v/src/common/riscv_vfork.c | 28 +++++++++++++++-----------
6 files changed, 86 insertions(+), 15 deletions(-)
diff --git a/arch/Kconfig b/arch/Kconfig
index f5a6b1ecb5..358ab72831 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -490,6 +490,41 @@ config ARCH_DPFPU
Enable toolchain support for double precision (64-bit) floating
point if both the toolchain and the hardware support it.
+config ARCH_LAZYFPU
+ bool "Enable lazy FPU state save / restore"
+ default n
+ depends on ARCH_FPU && ARCH_HAVE_LAZYFPU
+ ---help---
+ Enable lazy FPU state save and restore. Normally FPU state is saved
+ and restored with the integer context registers, if the task is using
+ FPU. The state is typically saved into the task's user stack upon
+ exception entry or context switch out, and restored when the
+ exception returns or context switches back in.
+
+ As the kernel does not use FPU, this can be optimized with the help
+ of the FPU hardware status and a bit of code logic inside the kernel.
+ The logic keeps track of the FPU state, which can be "unused",
+ "dirty" or "clean". A clean state means the FPU has not been used
+ since the last state save, while the dirty state indicates that the
+ FPU has been used.
+
+ The optimization saves / restores FPU registers only if:
+ - A context change has happened, save and restore does not happen
+ during exception entry / return to the same task
+ - FPU is in use (state is not unused) and
+ - FPU status is dirty, i.e. FPU has been used after the last
+ - FPU restore happens when status is in dirty or clean
+
+ This saves CPU time as the FPU registers do not have to be moved in
+ and out when handling an exception that does not result in a context
+ switch.
+
+ The tradeoff with the lazy FPU feature is that it requires a static
+ memory allocation from the task's TCB to store the FPU registers,
+ while the non-lazy style can use stack memory for storing the FPU
+ registers, saving memory as the stack frame for the FPU registers can
+ be skipped if the FPU is not in use.
+
config ARCH_USE_MMU
bool "Enable MMU"
default n
diff --git a/arch/risc-v/Kconfig b/arch/risc-v/Kconfig
index c45d64505c..9a8ec2aac0 100644
--- a/arch/risc-v/Kconfig
+++ b/arch/risc-v/Kconfig
@@ -216,6 +216,11 @@ config ARCH_RV_ISA_C
bool
default n
+config ARCH_HAVE_LAZYFPU
+ bool
+ default y
+ depends on ARCH_HAVE_FPU
+
config ARCH_FAMILY
string
default "rv32" if ARCH_RV32
diff --git a/arch/risc-v/include/irq.h b/arch/risc-v/include/irq.h
index 0ab74886e8..ae90c6bb67 100644
--- a/arch/risc-v/include/irq.h
+++ b/arch/risc-v/include/irq.h
@@ -247,9 +247,15 @@
#define XCPTCONTEXT_REGS (INT_XCPT_REGS + FPU_XCPT_REGS)
+#ifdef CONFIG_ARCH_LAZYFPU
/* Save only integer regs. FPU is handled separately */
#define XCPTCONTEXT_SIZE (INT_XCPT_SIZE)
+#else
+/* Save FPU registers with the integer registers */
+
+#define XCPTCONTEXT_SIZE (INT_XCPT_SIZE + FPU_XCPT_SIZE)
+#endif
/* In assembly language, values have to be referenced as byte address
* offsets. But in C, it is more convenient to reference registers as
@@ -570,7 +576,7 @@ struct xcptcontext
/* FPU register save area */
-#ifdef CONFIG_ARCH_FPU
+#if defined(CONFIG_ARCH_FPU) && defined(CONFIG_ARCH_LAZYFPU)
uintptr_t fregs[FPU_XCPT_SIZE];
#endif
};
diff --git a/arch/risc-v/src/common/riscv_fpu.S b/arch/risc-v/src/common/riscv_fpu.S
index 9d5feb9390..e773defd28 100644
--- a/arch/risc-v/src/common/riscv_fpu.S
+++ b/arch/risc-v/src/common/riscv_fpu.S
@@ -103,7 +103,11 @@ riscv_savefpu:
li t1, MSTATUS_FS
and t2, t0, t1
li t1, MSTATUS_FS_DIRTY
+#ifdef CONFIG_ARCH_LAZYFPU
bne t2, t1, 1f
+#else
+ blt t2, t1, 1f
+#endif
li t1, ~MSTATUS_FS
and t0, t0, t1
li t1, MSTATUS_FS_CLEAN
diff --git a/arch/risc-v/src/common/riscv_internal.h b/arch/risc-v/src/common/riscv_internal.h
index 18a8b9bb3b..50158eedec 100644
--- a/arch/risc-v/src/common/riscv_internal.h
+++ b/arch/risc-v/src/common/riscv_internal.h
@@ -206,10 +206,26 @@ void riscv_exception_attach(void);
void riscv_fpuconfig(void);
void riscv_savefpu(uintptr_t *regs, uintptr_t *fregs);
void riscv_restorefpu(uintptr_t *regs, uintptr_t *fregs);
+
+/* Get FPU register save area */
+
+static inline uintptr_t *riscv_fpuregs(struct tcb_s *tcb)
+{
+#ifdef CONFIG_ARCH_LAZYFPU
+ /* With lazy FPU the registers are simply in tcb */
+
+ return tcb->xcp.fregs;
+#else
+ /* Otherwise they are after the integer registers */
+
+ return (uintptr_t *)((uintptr_t)tcb->xcp.regs + INT_XCPT_SIZE);
+#endif
+}
#else
# define riscv_fpuconfig()
# define riscv_savefpu(regs, fregs)
# define riscv_restorefpu(regs, fregs)
+# define riscv_fpuregs(tcb)
#endif
/* Save / restore context of task */
@@ -219,9 +235,10 @@ static inline void riscv_savecontext(struct tcb_s *tcb)
tcb->xcp.regs = (uintptr_t *)CURRENT_REGS;
#ifdef CONFIG_ARCH_FPU
+
/* Save current process FPU state to TCB */
- riscv_savefpu(tcb->xcp.regs, tcb->xcp.fregs);
+ riscv_savefpu(tcb->xcp.regs, riscv_fpuregs(tcb));
#endif
}
@@ -242,7 +259,7 @@ static inline void riscv_restorecontext(struct tcb_s *tcb)
#ifdef CONFIG_ARCH_FPU
/* Restore FPU after the new address environment is instantiated */
- riscv_restorefpu(tcb->xcp.regs, tcb->xcp.fregs);
+ riscv_restorefpu(tcb->xcp.regs, riscv_fpuregs(tcb));
#endif
}
diff --git a/arch/risc-v/src/common/riscv_vfork.c b/arch/risc-v/src/common/riscv_vfork.c
index 4446b6b708..e0f30e41f7 100644
--- a/arch/risc-v/src/common/riscv_vfork.c
+++ b/arch/risc-v/src/common/riscv_vfork.c
@@ -108,6 +108,9 @@ pid_t up_vfork(const struct vfork_s *context)
uintptr_t newtop;
uintptr_t stacktop;
uintptr_t stackutil;
+#ifdef CONFIG_ARCH_FPU
+ uintptr_t *fregs;
+#endif
sinfo("s0:%" PRIxREG " s1:%" PRIxREG " s2:%" PRIxREG " s3:%" PRIxREG ""
" s4:%" PRIxREG "\n",
@@ -227,18 +230,19 @@ pid_t up_vfork(const struct vfork_s *context)
child->cmn.xcp.regs[REG_GP] = newsp; /* Global pointer */
#endif
#ifdef CONFIG_ARCH_FPU
- child->cmn.xcp.fregs[REG_FS0] = context->fs0; /* Saved register fs1 */
- child->cmn.xcp.fregs[REG_FS1] = context->fs1; /* Saved register fs1 */
- child->cmn.xcp.fregs[REG_FS2] = context->fs2; /* Saved register fs2 */
- child->cmn.xcp.fregs[REG_FS3] = context->fs3; /* Saved register fs3 */
- child->cmn.xcp.fregs[REG_FS4] = context->fs4; /* Saved register fs4 */
- child->cmn.xcp.fregs[REG_FS5] = context->fs5; /* Saved register fs5 */
- child->cmn.xcp.fregs[REG_FS6] = context->fs6; /* Saved register fs6 */
- child->cmn.xcp.fregs[REG_FS7] = context->fs7; /* Saved register fs7 */
- child->cmn.xcp.fregs[REG_FS8] = context->fs8; /* Saved register fs8 */
- child->cmn.xcp.fregs[REG_FS9] = context->fs9; /* Saved register fs9 */
- child->cmn.xcp.fregs[REG_FS10] = context->fs10; /* Saved register fs10 */
- child->cmn.xcp.fregs[REG_FS11] = context->fs11; /* Saved register fs11 */
+ fregs = riscv_fpuregs(&child->cmn);
+ fregs[REG_FS0] = context->fs0; /* Saved register fs1 */
+ fregs[REG_FS1] = context->fs1; /* Saved register fs1 */
+ fregs[REG_FS2] = context->fs2; /* Saved register fs2 */
+ fregs[REG_FS3] = context->fs3; /* Saved register fs3 */
+ fregs[REG_FS4] = context->fs4; /* Saved register fs4 */
+ fregs[REG_FS5] = context->fs5; /* Saved register fs5 */
+ fregs[REG_FS6] = context->fs6; /* Saved register fs6 */
+ fregs[REG_FS7] = context->fs7; /* Saved register fs7 */
+ fregs[REG_FS8] = context->fs8; /* Saved register fs8 */
+ fregs[REG_FS9] = context->fs9; /* Saved register fs9 */
+ fregs[REG_FS10] = context->fs10; /* Saved register fs10 */
+ fregs[REG_FS11] = context->fs11; /* Saved register fs11 */
#endif
#ifdef CONFIG_LIB_SYSCALL