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