This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 1d48b2d73cd1a513099bce8e62c8ba7f28efd04b
Author: Afonso Oliveira <[email protected]>
AuthorDate: Thu Oct 8 22:07:01 2026 +0100

    riscv/erbium: Emulate the 64-bit integer FCVT instructions.
    
    With the FPU enabled, GCC uses FCVT.L.S, FCVT.LU.S, FCVT.S.L and
    FCVT.S.LU for every conversion between float and a 64-bit integer type,
    and unlike -mno-fdiv for division, no option avoids them. NuttX's
    expf(), and through it logf(), powf() and other libm functions, use
    them too. ET-Minion has no hardware for these instructions and raises
    exception 30 (M-code emulation), so such code could not run on Erbium.
    
    Add CONFIG_ERBIUM_MCODE_EMULATION, enabled by default with the FPU.
    riscv_dispatch_irq() hands exception 30 to erbium_mcode_emulate()
    before riscv_doirq(), because the trap may be nested in an interrupt
    handler and riscv_doirq() would end the outer interrupt context.
    
    The trap handler fetches the instruction at EPC and makes the checks
    that the hardware makes for a native F instruction: mstatus.FS and the
    rounding mode. It performs the conversion with integer-only code, in
    every RISC-V rounding mode and with the RISC-V saturation and fflags
    rules. As ET-Minion does for FCVT.W.S, a subnormal input reads as zero
    and sets fflags bit 31. The result goes to the saved X register or the
    live F register, which the trap path does not save and which is reached
    through a table of FMV instructions. The FP state is marked dirty and
    execution resumes at the next instruction. Other M-code instructions
    still stop with the exception report.
    
    Assisted-by: Claude:claude-opus-5-5
    Signed-off-by: Afonso Oliveira <[email protected]>
---
 arch/risc-v/src/erbium/CMakeLists.txt              |   4 +
 arch/risc-v/src/erbium/Kconfig                     |  17 +
 arch/risc-v/src/erbium/Make.defs                   |   5 +
 arch/risc-v/src/erbium/erbium_irq_dispatch.c       |  28 +-
 arch/risc-v/src/erbium/erbium_mcode.c              | 502 +++++++++++++++++++++
 .../{erbium_irq_dispatch.c => erbium_mcode.h}      |  75 +--
 arch/risc-v/src/erbium/erbium_mcode_fpr.S          | 126 ++++++
 7 files changed, 705 insertions(+), 52 deletions(-)

diff --git a/arch/risc-v/src/erbium/CMakeLists.txt 
b/arch/risc-v/src/erbium/CMakeLists.txt
index 492a1c1d988..230f9f26cf2 100644
--- a/arch/risc-v/src/erbium/CMakeLists.txt
+++ b/arch/risc-v/src/erbium/CMakeLists.txt
@@ -23,4 +23,8 @@
 set(SRCS erbium_head.S erbium_start.c erbium_irq.c erbium_irq_dispatch.c
          erbium_timerisr.c erbium_serial.c)
 
+if(CONFIG_ERBIUM_MCODE_EMULATION)
+  list(APPEND SRCS erbium_mcode.c erbium_mcode_fpr.S)
+endif()
+
 target_sources(arch PRIVATE ${SRCS})
diff --git a/arch/risc-v/src/erbium/Kconfig b/arch/risc-v/src/erbium/Kconfig
index de2f898d1c7..dc1db90d3ba 100644
--- a/arch/risc-v/src/erbium/Kconfig
+++ b/arch/risc-v/src/erbium/Kconfig
@@ -20,3 +20,20 @@ config ERBIUM_MTIMER_FREQ
        ---help---
                Machine timer frequency after the reference clock divider and
                timer prescaler. The ET-platform emulator defaults to 2 MHz.
+
+config ERBIUM_MCODE_EMULATION
+       bool "Emulate the 64-bit integer FCVT instructions"
+       default y
+       depends on ARCH_FPU && !ARCH_USE_S_MODE
+       ---help---
+               ET-Minion has no hardware for FCVT.L.S, FCVT.LU.S, FCVT.S.L and
+               FCVT.S.LU, which GCC emits for every conversion between float 
and
+               a 64-bit integer type (long, int64_t, size_t, time_t). They 
raise
+               exception 30 (M-code emulation). With this option the trap 
handler
+               performs them in software, with the ET-Minion rule that a 
subnormal
+               input reads as zero and sets fflags bit 31, and resumes after 
the
+               instruction. Each conversion then costs a trap, about 260
+               instructions in erbium_emu. Without this option they stop the
+               system with an "M-code emulation" exception report. FDIV.S,
+               FSQRT.S, FENCE.I, SFENCE.VMA and the ET packed instructions are
+               never emulated.
diff --git a/arch/risc-v/src/erbium/Make.defs b/arch/risc-v/src/erbium/Make.defs
index b4657dfee5a..7612982ee5a 100644
--- a/arch/risc-v/src/erbium/Make.defs
+++ b/arch/risc-v/src/erbium/Make.defs
@@ -26,3 +26,8 @@ HEAD_ASRC = erbium_head.S
 
 CHIP_CSRCS  = erbium_start.c erbium_irq_dispatch.c erbium_irq.c
 CHIP_CSRCS += erbium_timerisr.c erbium_serial.c
+
+ifeq ($(CONFIG_ERBIUM_MCODE_EMULATION),y)
+CHIP_CSRCS += erbium_mcode.c
+CHIP_ASRCS += erbium_mcode_fpr.S
+endif
diff --git a/arch/risc-v/src/erbium/erbium_irq_dispatch.c 
b/arch/risc-v/src/erbium/erbium_irq_dispatch.c
index c37fdb161cd..c42bb47748f 100644
--- a/arch/risc-v/src/erbium/erbium_irq_dispatch.c
+++ b/arch/risc-v/src/erbium/erbium_irq_dispatch.c
@@ -25,11 +25,13 @@
  ****************************************************************************/
 
 #include <nuttx/config.h>
+#include <errno.h>
 #include <stdint.h>
 #include <nuttx/irq.h>
 
 #include "hardware/erbium_plic.h"
 #include "riscv_internal.h"
+#include "erbium_mcode.h"
 
 /****************************************************************************
  * Pre-processor Definitions
@@ -54,7 +56,9 @@
  *   Dispatch an exception as IRQ <cause> (0-31) and a hart-local
  *   interrupt as IRQ RISCV_IRQ_ASYNC + <cause>, or claim, dispatch and
  *   complete a PLIC interrupt. Complete nonzero claims even if their
- *   source is unsupported.
+ *   source is unsupported. With CONFIG_ERBIUM_MCODE_EMULATION, first try
+ *   to emulate an instruction that raised exception 30 (M-code
+ *   emulation).
  *
  * Input Parameters:
  *   vector - Trap cause register value.
@@ -74,6 +78,28 @@ void *riscv_dispatch_irq(uintreg_t vector, uintreg_t *regs)
     {
       irq += RISCV_IRQ_ASYNC;
     }
+#ifdef CONFIG_ERBIUM_MCODE_EMULATION
+  else if (irq == ERBIUM_IRQ_MCODE)
+    {
+      /* Emulate the instruction and resume after it. Do not go through
+       * riscv_doirq(): the trap may be nested in an interrupt handler, and
+       * riscv_doirq() would end the outer interrupt context and clear the
+       * running task's saved-register pointer.
+       */
+
+      int ret = erbium_mcode_emulate(regs);
+
+      if (ret == OK)
+        {
+          return regs;
+        }
+
+      if (ret == -EINVAL)
+        {
+          irq = RISCV_IRQ_IINSTRUCTION;
+        }
+    }
+#endif
 
   if (irq == RISCV_IRQ_EXT)
     {
diff --git a/arch/risc-v/src/erbium/erbium_mcode.c 
b/arch/risc-v/src/erbium/erbium_mcode.c
new file mode 100644
index 00000000000..b5cc3b00ae5
--- /dev/null
+++ b/arch/risc-v/src/erbium/erbium_mcode.c
@@ -0,0 +1,502 @@
+/****************************************************************************
+ * arch/risc-v/src/erbium/erbium_mcode.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* ET-Minion raises exception 30 (M-code emulation) for FCVT.L.S,
+ * FCVT.LU.S, FCVT.S.L and FCVT.S.LU instead of executing them. This file
+ * performs them on the trapped context with RISC-V F semantics and the
+ * ET-Minion input rule for subnormals (read as zero, flagged in fflags
+ * bit 31), as the hardware does for FCVT.W.S.
+ *
+ * Keep this file integer-only: a float, a double or a conversion between
+ * floating point and a 64-bit integer would compile to the instructions it
+ * emulates, and any FP register use would clobber the interrupted
+ * context's FP state, which the trap path does not save.
+ */
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <arch/irq.h>
+
+#include "riscv_internal.h"
+#include "erbium_mcode.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* OP-FP, fmt S, rs2 2 (L) or 3 (LU); the mask keeps funct7, rs2, opcode */
+
+#define FCVT_MASK          0xfff0007f
+#define FCVT_L_S           0xc0200053
+#define FCVT_LU_S          0xc0300053
+#define FCVT_S_L           0xd0200053
+#define FCVT_S_LU          0xd0300053
+#define FCVT_UNSIGNED      (1u << 20)     /* rs2 bit 0: the LU variants */
+
+#define INSN_RD(i)         (((i) >> 7) & 0x1f)
+#define INSN_RM(i)         (((i) >> 12) & 0x7)
+#define INSN_RS1(i)        (((i) >> 15) & 0x1f)
+
+#define XREG_ZERO          0
+#define XREG_GP            3
+
+#define FRM_RNE            0
+#define FRM_RTZ            1
+#define FRM_RDN            2
+#define FRM_RUP            3
+#define FRM_RMM            4
+#define FRM_DYN            7
+
+#define FFLAG_NX           0x01u
+#define FFLAG_NV           0x10u
+#define FFLAG_INPUTDENORM  0x80000000u    /* ET-Minion fflags bit 31 */
+
+/* IEEE 754 binary32. A normal value with biased exponent e and fraction f
+ * has magnitude (2^23 + f) * 2^(e - 150); e = 255 encodes Inf and NaN.
+ */
+
+#define F32_FRAC_BITS      23
+#define F32_BIAS           127
+#define F32_EXP_INFNAN     0xff
+#define F32_HIDDEN         (UINT64_C(1) << F32_FRAC_BITS)
+#define F32_SIGN(x)        (((x) >> 31) != 0)
+#define F32_EXP(x)         (((x) >> F32_FRAC_BITS) & 0xff)
+#define F32_FRAC(x)        ((x) & 0x7fffff)
+#define F32_SIGN_BIT       0x80000000u
+
+#define I64_MIN_BITS       UINT64_C(0x8000000000000000)
+#define I64_MAX_BITS       UINT64_C(0x7fffffffffffffff)
+
+/* The trap frame holds xN in regs[N] for N = 1..31; regs[0] is the EPC */
+
+static_assert(REG_X1 == 1 && REG_X31 == 31, "Unexpected trap frame");
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: erbium_msb64
+ *
+ * Description:
+ *   Find the most significant set bit of a 64-bit value.
+ *
+ * Input Parameters:
+ *   val - The value, which must not be zero.
+ *
+ * Returned Value:
+ *   The bit index, 0 to 63.
+ *
+ ****************************************************************************/
+
+static unsigned int erbium_msb64(uint64_t val)
+{
+  unsigned int msb = 0;
+  unsigned int step;
+
+  for (step = 32; step != 0; step >>= 1)
+    {
+      if ((val >> step) != 0)
+        {
+          val >>= step;
+          msb += step;
+        }
+    }
+
+  return msb;
+}
+
+/****************************************************************************
+ * Name: erbium_round_up
+ *
+ * Description:
+ *   Decide whether a magnitude truncated to an integer must be incremented
+ *   to round it in a RISC-V rounding mode.
+ *
+ * Input Parameters:
+ *   rm   - Rounding mode, FRM_RNE to FRM_RMM.
+ *   sign - True if the value is negative.
+ *   mag  - The truncated magnitude.
+ *   rem  - The discarded bits.
+ *   half - The weight of the highest discarded bit.
+ *
+ * Returned Value:
+ *   True if the magnitude must be incremented.
+ *
+ ****************************************************************************/
+
+static bool erbium_round_up(unsigned int rm, bool sign, uint64_t mag,
+                            uint64_t rem, uint64_t half)
+{
+  switch (rm)
+    {
+      case FRM_RNE:
+        return rem > half || (rem == half && (mag & 1) != 0);
+
+      case FRM_RDN:
+        return sign && rem != 0;
+
+      case FRM_RUP:
+        return !sign && rem != 0;
+
+      case FRM_RMM:
+        return rem >= half;
+
+      default:                                    /* FRM_RTZ */
+        return false;
+    }
+}
+
+/****************************************************************************
+ * Name: erbium_x64_invalid
+ *
+ * Description:
+ *   Raise the invalid-operation flag for a float to 64-bit integer
+ *   conversion whose result does not fit, and saturate the result.
+ *
+ * Input Parameters:
+ *   sign        - True if the value is negative, false for NaN.
+ *   is_unsigned - True for FCVT.LU.S.
+ *   fflags      - Accumulated exception flags.
+ *
+ * Returned Value:
+ *   The representable integer closest to the value; the largest one for
+ *   NaN.
+ *
+ ****************************************************************************/
+
+static uint64_t erbium_x64_invalid(bool sign, bool is_unsigned,
+                                   uint32_t *fflags)
+{
+  *fflags |= FFLAG_NV;                      /* NV alone, never with NX */
+
+  if (is_unsigned)
+    {
+      return sign ? 0 : UINT64_MAX;
+    }
+
+  return sign ? I64_MIN_BITS : I64_MAX_BITS;
+}
+
+/****************************************************************************
+ * Name: erbium_f32_to_x64
+ *
+ * Description:
+ *   Perform FCVT.L.S or FCVT.LU.S.
+ *
+ * Input Parameters:
+ *   bits        - The single-precision source operand.
+ *   rm          - Rounding mode, FRM_RNE to FRM_RMM.
+ *   is_unsigned - True for FCVT.LU.S.
+ *   fflags      - Accumulated exception flags.
+ *
+ * Returned Value:
+ *   The 64-bit integer result.
+ *
+ ****************************************************************************/
+
+static uint64_t erbium_f32_to_x64(uint32_t bits, unsigned int rm,
+                                  bool is_unsigned, uint32_t *fflags)
+{
+  bool sign = F32_SIGN(bits);
+  unsigned int exp = F32_EXP(bits);
+  uint64_t sig = F32_FRAC(bits) | F32_HIDDEN;
+  uint64_t limit;
+  uint64_t mag;
+  uint64_t rem = 0;
+  unsigned int shift;
+
+  if (exp == 0)
+    {
+      /* Zero, or a subnormal that ET-Minion reads as zero and flags */
+
+      if (F32_FRAC(bits) != 0)
+        {
+          *fflags |= FFLAG_INPUTDENORM;
+        }
+
+      return 0;
+    }
+
+  if (exp == F32_EXP_INFNAN && F32_FRAC(bits) != 0)
+    {
+      sign = false;                         /* NaN converts like +Inf */
+    }
+
+  if (exp >= F32_BIAS + 64)                 /* |x| >= 2^64, Inf or NaN */
+    {
+      return erbium_x64_invalid(sign, is_unsigned, fflags);
+    }
+
+  if (exp >= F32_BIAS + F32_FRAC_BITS)      /* Integral, exact */
+    {
+      mag = sig << (exp - F32_BIAS - F32_FRAC_BITS);
+    }
+  else if (exp >= F32_BIAS - 1)             /* 0.5 <= |x| < 2^23 */
+    {
+      shift = F32_BIAS + F32_FRAC_BITS - exp; /* 1..24 */
+      mag = sig >> shift;
+      rem = sig & ((UINT64_C(1) << shift) - 1);
+      if (erbium_round_up(rm, sign, mag, rem, UINT64_C(1) << (shift - 1)))
+        {
+          mag++;
+        }
+    }
+  else                                      /* 0 < |x| < 0.5 */
+    {
+      mag = 0;
+      rem = 1;                              /* Nonzero, below one half */
+      if (erbium_round_up(rm, sign, 0, rem, 2))
+        {
+          mag = 1;
+        }
+    }
+
+  /* The largest magnitude that the destination holds with this sign */
+
+  if (is_unsigned)
+    {
+      limit = sign ? 0 : UINT64_MAX;
+    }
+  else
+    {
+      limit = sign ? I64_MIN_BITS : I64_MAX_BITS;
+    }
+
+  if (mag > limit)
+    {
+      return erbium_x64_invalid(sign, is_unsigned, fflags);
+    }
+
+  if (rem != 0)
+    {
+      *fflags |= FFLAG_NX;
+    }
+
+  return sign ? 0 - mag : mag;
+}
+
+/****************************************************************************
+ * Name: erbium_x64_to_f32
+ *
+ * Description:
+ *   Perform FCVT.S.L or FCVT.S.LU.
+ *
+ * Input Parameters:
+ *   val         - The 64-bit integer source operand.
+ *   rm          - Rounding mode, FRM_RNE to FRM_RMM.
+ *   is_unsigned - True for FCVT.S.LU.
+ *   fflags      - Accumulated exception flags.
+ *
+ * Returned Value:
+ *   The single-precision result.
+ *
+ ****************************************************************************/
+
+static uint32_t erbium_x64_to_f32(uint64_t val, unsigned int rm,
+                                  bool is_unsigned, uint32_t *fflags)
+{
+  bool sign = !is_unsigned && (int64_t)val < 0;
+  uint64_t mag = sign ? 0 - val : val;
+  unsigned int msb;
+  unsigned int exp;
+  unsigned int shift;
+  uint64_t sig;
+  uint64_t rem;
+
+  if (mag == 0)
+    {
+      return 0;                             /* +0.0 in every mode */
+    }
+
+  msb = erbium_msb64(mag);
+  exp = F32_BIAS + msb;
+
+  if (msb <= F32_FRAC_BITS)
+    {
+      sig = mag << (F32_FRAC_BITS - msb);   /* Exact */
+    }
+  else
+    {
+      shift = msb - F32_FRAC_BITS;          /* 1..40 */
+      sig = mag >> shift;
+      rem = mag & ((UINT64_C(1) << shift) - 1);
+      if (rem != 0)
+        {
+          *fflags |= FFLAG_NX;
+          if (erbium_round_up(rm, sign, sig, rem,
+                              UINT64_C(1) << (shift - 1)) &&
+              ++sig == (F32_HIDDEN << 1))
+            {
+              sig >>= 1;                    /* Carry into the exponent */
+              exp++;
+            }
+        }
+    }
+
+  return (sign ? F32_SIGN_BIT : 0) | (exp << F32_FRAC_BITS) |
+         (uint32_t)F32_FRAC(sig);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: erbium_mcode_emulate
+ *
+ * Description:
+ *   Perform the instruction that raised exception 30 (M-code emulation)
+ *   on the saved context and advance its EPC. Only FCVT.L.S, FCVT.LU.S,
+ *   FCVT.S.L and FCVT.S.LU are emulated.
+ *
+ * Input Parameters:
+ *   regs - Saved registers of the trapped context.
+ *
+ * Returned Value:
+ *   OK if the instruction was emulated; -EINVAL if it must raise
+ *   illegal-instruction instead; -ENOSYS if it is not emulated.
+ *
+ ****************************************************************************/
+
+int erbium_mcode_emulate(uintreg_t *regs)
+{
+  uintptr_t epc = regs[REG_EPC];
+  uint32_t fflags = 0;
+  bool fp_written = false;
+  bool is_unsigned;
+  unsigned int rd;
+  unsigned int rs1;
+  unsigned int rm;
+  uint32_t insn;
+
+  /* Fetch the instruction. mtval holds it on erbium_emu, but the Erbium
+   * documentation does not specify mtval for exception 30. With the C
+   * extension a 32-bit instruction may be only 2-byte aligned, and a 16-bit
+   * instruction (possible only through minstmask) must not be read past.
+   */
+
+  insn = *(FAR volatile const uint16_t *)epc;
+  if ((insn & 3) != 3)
+    {
+      return -ENOSYS;
+    }
+
+  insn |= (uint32_t)*(FAR volatile const uint16_t *)(epc + 2) << 16;
+
+  switch (insn & FCVT_MASK)
+    {
+      case FCVT_L_S:
+      case FCVT_LU_S:
+      case FCVT_S_L:
+      case FCVT_S_LU:
+        break;
+
+      default:
+        return -ENOSYS;   /* FDIV.S, FSQRT.S, FENCE.I, packed ops, ... */
+    }
+
+  rd          = INSN_RD(insn);
+  rs1         = INSN_RS1(insn);
+  rm          = INSN_RM(insn);
+  is_unsigned = (insn & FCVT_UNSIGNED) != 0;
+
+  /* Exception 30 outranks the illegal-instruction checks that a native F
+   * instruction makes (FPU off, reserved rounding mode). Make them here,
+   * before touching any FP register or CSR.
+   */
+
+  if ((regs[REG_INT_CTX] & MSTATUS_FS) == 0)
+    {
+      return -EINVAL;
+    }
+
+  if (rm == FRM_DYN)
+    {
+      rm = READ_CSR(CSR_FRM);
+    }
+
+  if (rm > FRM_RMM)
+    {
+      return -EINVAL;
+    }
+
+  if ((insn & FCVT_MASK) == FCVT_L_S || (insn & FCVT_MASK) == FCVT_LU_S)
+    {
+      uint64_t res;
+
+      if (rd == XREG_GP)
+        {
+          return -ENOSYS;  /* gp is not restored from the trap frame */
+        }
+
+      res = erbium_f32_to_x64(erbium_mcode_getfpr(rs1), rm, is_unsigned,
+                              &fflags);
+      if (rd != XREG_ZERO)
+        {
+          regs[rd] = res;  /* Never regs[0], which is the EPC */
+        }
+    }
+  else
+    {
+      uint64_t src = 0;
+
+      if (rs1 == XREG_GP)
+        {
+          __asm__ __volatile__("mv %0, gp" : "=r"(src));
+        }
+      else if (rs1 != XREG_ZERO)
+        {
+          src = regs[rs1]; /* For sp, the frame holds the pre-trap value */
+        }
+
+      erbium_mcode_setfpr(rd, erbium_x64_to_f32(src, rm, is_unsigned,
+                                                &fflags));
+      fp_written = true;
+    }
+
+  if (fflags != 0)
+    {
+      SET_CSR(CSR_FFLAGS, (uintreg_t)fflags);
+    }
+
+  /* The trap return reloads mstatus from the frame. Record the FP state
+   * change there, or a lazy FPU context save would skip it.
+   */
+
+  if (fp_written || fflags != 0)
+    {
+      regs[REG_INT_CTX] |= MSTATUS_FS_DIRTY;
+    }
+
+  regs[REG_EPC] = epc + 4;  /* None of the four has a compressed form */
+  return OK;
+}
diff --git a/arch/risc-v/src/erbium/erbium_irq_dispatch.c 
b/arch/risc-v/src/erbium/erbium_mcode.h
similarity index 50%
copy from arch/risc-v/src/erbium/erbium_irq_dispatch.c
copy to arch/risc-v/src/erbium/erbium_mcode.h
index c37fdb161cd..ca5addb383c 100644
--- a/arch/risc-v/src/erbium/erbium_irq_dispatch.c
+++ b/arch/risc-v/src/erbium/erbium_mcode.h
@@ -1,5 +1,5 @@
 /****************************************************************************
- * arch/risc-v/src/erbium/erbium_irq_dispatch.c
+ * arch/risc-v/src/erbium/erbium_mcode.h
  *
  * SPDX-License-Identifier: Apache-2.0
  *
@@ -20,78 +20,51 @@
  *
  ****************************************************************************/
 
+#ifndef __ARCH_RISCV_SRC_ERBIUM_ERBIUM_MCODE_H
+#define __ARCH_RISCV_SRC_ERBIUM_ERBIUM_MCODE_H
+
 /****************************************************************************
  * Included Files
  ****************************************************************************/
 
 #include <nuttx/config.h>
-#include <stdint.h>
-#include <nuttx/irq.h>
-
-#include "hardware/erbium_plic.h"
-#include "riscv_internal.h"
 
-/****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
+#include <stdint.h>
 
-/* An exception is dispatched with its 5-bit cause code as the IRQ number,
- * so every code must lie below the first interrupt IRQ number.
- */
+#include <arch/irq.h>
 
-#if RISCV_IRQ_ASYNC < 32
-#  error "ET-Minion exception codes would alias interrupt IRQ numbers"
-#endif
+#ifdef CONFIG_ERBIUM_MCODE_EMULATION
 
 /****************************************************************************
- * Public Functions
+ * Public Function Prototypes
  ****************************************************************************/
 
 /****************************************************************************
- * Name: riscv_dispatch_irq
+ * Name: erbium_mcode_emulate
  *
  * Description:
- *   Dispatch an exception as IRQ <cause> (0-31) and a hart-local
- *   interrupt as IRQ RISCV_IRQ_ASYNC + <cause>, or claim, dispatch and
- *   complete a PLIC interrupt. Complete nonzero claims even if their
- *   source is unsupported.
+ *   Perform the instruction that raised exception 30 (M-code emulation)
+ *   on the saved context and advance its EPC. Runs in trap context with
+ *   interrupts disabled and uses no OS service.
  *
  * Input Parameters:
- *   vector - Trap cause register value.
- *   regs - Saved registers for the interrupted task.
+ *   regs - Saved registers of the trapped context.
  *
  * Returned Value:
- *   The register context to restore, possibly belonging to another task.
+ *   OK if the instruction was emulated; -EINVAL if it must raise
+ *   illegal-instruction instead (FPU off or reserved rounding mode);
+ *   -ENOSYS if it is not emulated.
  *
  ****************************************************************************/
 
-void *riscv_dispatch_irq(uintreg_t vector, uintreg_t *regs)
-{
-  int irq = vector & RISCV_IRQ_MASK;
-  uint32_t claim;
+int erbium_mcode_emulate(uintreg_t *regs);
 
-  if ((vector & RISCV_IRQ_BIT) != 0)
-    {
-      irq += RISCV_IRQ_ASYNC;
-    }
-
-  if (irq == RISCV_IRQ_EXT)
-    {
-      claim = getreg32(ERBIUM_PLIC_CLAIM);
-      if (claim != 0)
-        {
-          if (claim <= ERBIUM_PLIC_NDEV)
-            {
-              regs = riscv_doirq(RISCV_IRQ_EXT + claim, regs);
-            }
+/* Live FP register access (erbium_mcode_fpr.S). The trap path does not
+ * save the F registers, so the emulator reads and writes them in place.
+ */
 
-          putreg32(claim, ERBIUM_PLIC_CLAIM);
-        }
-    }
-  else
-    {
-      regs = riscv_doirq(irq, regs);
-    }
+uint32_t erbium_mcode_getfpr(unsigned int reg);
+void erbium_mcode_setfpr(unsigned int reg, uint32_t val);
 
-  return regs;
-}
+#endif /* CONFIG_ERBIUM_MCODE_EMULATION */
+#endif /* __ARCH_RISCV_SRC_ERBIUM_ERBIUM_MCODE_H */
diff --git a/arch/risc-v/src/erbium/erbium_mcode_fpr.S 
b/arch/risc-v/src/erbium/erbium_mcode_fpr.S
new file mode 100644
index 00000000000..32cb3f06b22
--- /dev/null
+++ b/arch/risc-v/src/erbium/erbium_mcode_fpr.S
@@ -0,0 +1,126 @@
+/****************************************************************************
+ * arch/risc-v/src/erbium/erbium_mcode_fpr.S
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/* The trap path does not save the F registers and an FMV instruction
+ * encodes its register number as an immediate, so the M-code emulator
+ * reaches the live registers through tables of 8-byte FMV + RET pairs.
+ * These are the only FP instructions on the emulation path. The table is
+ * entered through t1, because a jump through t0 would be a return hint.
+ *
+ * fmv.x.w sign-extends bit 31, which is how the LP64 ABI returns a
+ * uint32_t. On ET-Minion fmv.w.x clears bits 32-255 of the register, as
+ * the scalar instruction being emulated would.
+ */
+
+/****************************************************************************
+ * Public Symbols
+ ****************************************************************************/
+
+  .globl  erbium_mcode_getfpr
+  .globl  erbium_mcode_setfpr
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+  .text
+  .option push
+  .option norvc
+
+/****************************************************************************
+ * Name: erbium_mcode_getfpr
+ *
+ * Description:
+ *   Read the low 32 bits of a live F register.
+ *
+ * C Function Prototype:
+ *   uint32_t erbium_mcode_getfpr(unsigned int reg);
+ *
+ * Input Parameters:
+ *   reg - F register number, 0 to 31.
+ *
+ * Returned Value:
+ *   The register's single-precision value.
+ *
+ ****************************************************************************/
+
+  .balign 8
+  .type   erbium_mcode_getfpr, @function
+
+erbium_mcode_getfpr:
+  andi    a0, a0, 31
+  slli    a0, a0, 3
+  lla     t1, 1f
+  add     t1, t1, a0
+  jr      t1
+  .balign 8
+1:
+  .irp    n, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+  fmv.x.w a0, f\n
+  ret
+  .endr
+  .irp    n, 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
+  fmv.x.w a0, f\n
+  ret
+  .endr
+  .size   erbium_mcode_getfpr, . - erbium_mcode_getfpr
+
+/****************************************************************************
+ * Name: erbium_mcode_setfpr
+ *
+ * Description:
+ *   Write a single-precision value to a live F register.
+ *
+ * C Function Prototype:
+ *   void erbium_mcode_setfpr(unsigned int reg, uint32_t val);
+ *
+ * Input Parameters:
+ *   reg - F register number, 0 to 31.
+ *   val - The single-precision value.
+ *
+ * Returned Value:
+ *   None.
+ *
+ ****************************************************************************/
+
+  .balign 8
+  .type   erbium_mcode_setfpr, @function
+
+erbium_mcode_setfpr:
+  andi    a0, a0, 31
+  slli    a0, a0, 3
+  lla     t1, 1f
+  add     t1, t1, a0
+  jr      t1
+  .balign 8
+1:
+  .irp    n, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
+  fmv.w.x f\n, a1
+  ret
+  .endr
+  .irp    n, 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
+  fmv.w.x f\n, a1
+  ret
+  .endr
+  .size   erbium_mcode_setfpr, . - erbium_mcode_setfpr
+
+  .option pop

Reply via email to