Skip to content

Instantly share code, notes, and snippets.

@sorear
Created September 7, 2025 19:29
Show Gist options
  • Select an option

  • Save sorear/45b6093194f3822e1529a18dcfd41a70 to your computer and use it in GitHub Desktop.

Select an option

Save sorear/45b6093194f3822e1529a18dcfd41a70 to your computer and use it in GitHub Desktop.
Abandoned OpenSBI F/D/Zfh emulator
commit 6de9afefb60318bd0527af440f9da08745b81ee7
Author: Stefan O'Rear <sorear@fastmail.com>
Date: Fri Mar 22 14:29:40 2024 -0400
floats
diff --git a/include/sbi/sbi_float_ldst.h b/include/sbi/sbi_float_ldst.h
new file mode 100644
index 0000000..919815a
--- /dev/null
+++ b/include/sbi/sbi_float_ldst.h
@@ -0,0 +1,31 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * XXX
+ */
+
+#ifndef __SBI_FLOAT_H__
+#define __SBI_FLOAT_H__
+
+#include <sbi/sbi_types.h>
+
+struct sbi_trap_regs;
+
+u64 sbi_float_get_f64(ulong reg);
+void sbi_float_set_f64(ulong reg, u64 value);
+ulong sbi_float_get_csr();
+void sbi_float_get_csr(ulong value);
+
+/* Illegal instruction handler for OP-LOAD-FP */
+int sbi_float_load_insn(ulong insn, struct sbi_trap_regs *regs);
+
+/* Illegal instruction handler for OP-STORE-FP */
+int sbi_float_store_insn(ulong insn, struct sbi_trap_regs *regs);
+
+/* Initialize floating point state */
+int sbi_float_init(struct sbi_scratch *scratch, bool cold_boot);
+
+#endif
diff --git a/include/sbi/sbi_float_op.h b/include/sbi/sbi_float_op.h
new file mode 100644
index 0000000..b4d34d9
--- /dev/null
+++ b/include/sbi/sbi_float_op.h
@@ -0,0 +1,20 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * XXX
+ */
+
+#ifndef __SBI_FLOAT_OP_H__
+#define __SBI_FLOAT_OP_H__
+
+#include <sbi/sbi_types.h>
+
+struct sbi_trap_regs;
+
+/* Illegal instruction handler for OP-FP and FMAs */
+int sbi_float_operate_insn(ulong insn, struct sbi_trap_regs *regs);
+
+#endif
diff --git a/lib/sbi/Kconfig b/lib/sbi/Kconfig
index df74bba..f1fa22a 100644
--- a/lib/sbi/Kconfig
+++ b/lib/sbi/Kconfig
@@ -34,4 +34,8 @@ config SBI_ECALL_VENDOR
bool "Platform-defined vendor extensions"
default y
+config FP_EMULATION
+ bool "Emulate FD_Zfh using trap handlers"
+ default y
+
endmenu
diff --git a/lib/sbi/sbi_emulate_csr.c b/lib/sbi/sbi_emulate_csr.c
index aec9d3c..ae9eb59 100644
--- a/lib/sbi/sbi_emulate_csr.c
+++ b/lib/sbi/sbi_emulate_csr.c
@@ -13,6 +13,7 @@
#include <sbi/sbi_console.h>
#include <sbi/sbi_emulate_csr.h>
#include <sbi/sbi_error.h>
+#include <sbi/sbi_float_ldst.h>
#include <sbi/sbi_hart.h>
#include <sbi/sbi_scratch.h>
#include <sbi/sbi_timer.h>
@@ -146,6 +147,18 @@ int sbi_emulate_csr_read(int csr_num, struct sbi_trap_regs *regs,
#undef switchcase_hpm_2
#undef switchcase_hpm
+#ifdef CONFIG_FP_EMULATION
+ case CSR_FCSR:
+ *csr_val = sbi_float_get_csr();
+ break;
+ case CSR_FRM:
+ *csr_val = sbi_float_get_csr() >> 5;
+ break;
+ case CSR_FFLAGS:
+ *csr_val = sbi_float_get_csr() & 31;
+ break;
+#endif
+
default:
ret = SBI_ENOTSUPP;
break;
@@ -184,6 +197,18 @@ int sbi_emulate_csr_write(int csr_num, struct sbi_trap_regs *regs,
ret = SBI_ENOTSUPP;
break;
#endif
+#ifdef CONFIG_FP_EMULATION
+ case CSR_FCSR:
+ sbi_float_set_csr(csr_val & 0xFF);
+ break;
+ case CSR_FRM:
+ sbi_float_set_csr((csr_val & 7) << 5 | sbi_float_get_csr() & 31);
+ break;
+ case CSR_FFLAGS:
+ sbi_float_set_csr(csr_val & 31 | sbi_float_get_csr() & ~31);
+ break;
+#endif
+
default:
ret = SBI_ENOTSUPP;
break;
diff --git a/lib/sbi/sbi_float_ldst.c b/lib/sbi/sbi_float_ldst.c
new file mode 100644
index 0000000..5582e29
--- /dev/null
+++ b/lib/sbi/sbi_float_ldst.c
@@ -0,0 +1,238 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * XXX
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/riscv_fp.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_float_ldst.h>
+#include <sbi/sbi_pmu.h>
+#include <sbi/sbi_trap.h>
+#include <sbi/sbi_unpriv.h>
+
+struct sbi_float_regs {
+ ulong fcsr;
+ u64 f[32];
+};
+
+union reg_data {
+ u8 data_bytes[8];
+ u16 data_u16;
+ u32 data_u32;
+ u64 data_u64;
+};
+
+u64 sbi_float_get_f64(ulong reg)
+{
+ if (misa_extension('D')) {
+ return GET_F64_REG(reg, 0, 0);
+ } else {
+ struct sbi_float_regs *regs = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(), float_regs_off);
+ return regs->f[reg];
+ }
+}
+
+void sbi_float_set_f64(ulong reg, u64 value)
+{
+ if (misa_extension('D')) {
+ SET_F64_REG(reg, 0, 0, value);
+ } else {
+ struct sbi_float_regs *regs = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(), float_regs_off);
+ regs->f[reg] = value;
+ SET_FS_DIRTY();
+ }
+}
+
+ulong sbi_float_get_fcsr()
+{
+ if (misa_extension('D')) {
+ return csr_read(CSR_FCSR);
+ } else {
+ struct sbi_float_regs *regs = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(), float_regs_off);
+ return regs->fcsr;
+ }
+}
+
+void sbi_float_set_csr(ulong value)
+{
+ if (misa_extension('D')) {
+ csr_write(CSR_FCSR, value);
+ } else {
+ struct sbi_float_regs *regs = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(), float_regs_off);
+ regs->fcsr = value;
+ SET_FS_DIRTY();
+ }
+}
+
+static ulong sbi_float_synthesize_tinst(ulong cause, ulong insn, ulong len, ulong reg)
+{
+ ulong log2 = len == 8 ? 3 : len == 4 ? 2 : 1;
+
+ switch (cause) {
+ case CAUSE_LOAD_ACCESS:
+ case CAUSE_LOAD_GUEST_PAGE_FAULT:
+ case CAUSE_LOAD_PAGE_FAULT:
+ return (INSN_IS_32BIT(insn) ? 0x07 : 0x05) | (log2 << 12) | (reg << SH_RD);
+ case CAUSE_STORE_ACCESS:
+ case CAUSE_STORE_GUEST_PAGE_FAULT:
+ case CAUSE_STORE_PAGE_FAULT:
+ return (INSN_IS_32BIT(insn) ? 0x27 : 0x25) | (log2 << 12) | (reg << SH_RS2);
+ default:
+ return 0;
+ }
+}
+
+int sbi_float_load_insn(ulong insn, struct sbi_trap_regs *regs)
+{
+ ulong reg, len, addr;
+ union reg_data val;
+
+ reg = REG_OFFSET(insn, SH_RD);
+ addr = GET_RS1(insn, regs) + IMM_I(insn);
+
+ if (IS_FS_OFF(regs))
+ return truly_illegal_insn(insn, regs);
+
+ if ((insn & INSN_MASK_FLD) == INSN_MATCH_FLD) {
+ len = 8;
+ } else if ((insn & INSN_MASK_FLW) == INSN_MATCH_FLW) {
+ len = 4;
+ } else if ((insn & INSN_MASK_FLH) == INSN_MATCH_FLH) {
+ len = 2;
+#ifdef __riscv_compressed
+ } else if ((insn & INSN_MASK_C_FLD) == INSN_MATCH_C_FLD) {
+ len = 8;
+ reg = RVC_RS2S(insn);
+ addr = GET_RS1S(insn, regs) + RVC_LD_IMM(insn);
+ } else if ((insn & INSN_MASK_C_FLDSP) == INSN_MATCH_C_FLDSP) {
+ len = 8;
+ addr = GET_SP(regs) + RVC_LDSP_IMM(insn);
+#if __riscv_xlen == 32
+ } else if ((insn & INSN_MASK_C_FLW) == INSN_MATCH_C_FLW) {
+ len = 4;
+ reg = RVC_RS2S(insn);
+ addr = GET_SP(regs) + RVC_LW_IMM(insn);
+ } else if ((insn & INSN_MASK_C_FLWSP) == INSN_MATCH_C_FLWSP) {
+ len = 4;
+ addr = GET_SP(regs) + RVC_LWSP_IMM(insn);
+#endif
+#endif
+ } else {
+ return truly_illegal_insn(insn, regs);
+ }
+
+ val.data_u64 = ~0;
+ if (addr & (len - 1)) {
+ for (i = 0; i < len; i++) {
+ val.data_bytes[i] = sbi_load_u8((void *)(addr + i),
+ &uptrap);
+ if (uptrap.cause) break;
+ }
+ } else if (len == 8) {
+ val.data_u64 = sbi_load_u64((void *)addr, &uptrap);
+ } else if (len == 4) {
+ val.data_u32 = sbi_load_u32((void *)addr, &uptrap);
+ } else {
+ val.data_u16 = sbi_load_u16((void *)addr, &uptrap);
+ }
+
+ if (uptrap.cause) {
+ uptrap.epc = regs->mepc;
+ uptrap.tinst = sbi_float_synthesize_tinst(uptrap.cause, insn, len, reg);
+ return sbi_trap_redirect(regs, &uptrap);
+ }
+
+ sbi_float_set_f64(reg, val.data_u64);
+ regs->mepc += INSN_LEN(insn);
+ sbi_pmu_ctr_incr_fw(SBI_PMU_FW_EMULATE_FLOAT_LOAD);
+
+ return 0;
+}
+
+int sbi_float_store_insn(ulong insn, struct sbi_trap_regs *regs)
+{
+ ulong reg, len, addr;
+ union reg_data val;
+
+ reg = REG_OFFSET(insn, SH_RS2);
+ addr = GET_RS1(insn, regs) + IMM_S(insn);
+
+ if (IS_FS_OFF(regs))
+ return truly_illegal_insn(insn, regs);
+
+ if ((insn & INSN_MASK_FSD) == INSN_MATCH_FSD) {
+ len = 8;
+ } else if ((insn & INSN_MASK_FSW) == INSN_MATCH_FSW) {
+ len = 4;
+ } else if ((insn & INSN_MASK_FSH) == INSN_MATCH_FSH) {
+ len = 2;
+#ifdef __riscv_compressed
+ } else if ((insn & INSN_MASK_C_FSD) == INSN_MATCH_C_FSD) {
+ len = 8;
+ reg = RVC_RS2S(insn);
+ addr = GET_RS1S(insn, regs) + RVC_LD_IMM(insn);
+ } else if ((insn & INSN_MASK_C_FSDSP) == INSN_MATCH_C_FSDSP) {
+ len = 8;
+ reg = REG_OFFSET(insn, SH_RS2C);
+ addr = GET_SP(regs) + RVC_SDSP_IMM(insn);
+#if __riscv_xlen == 32
+ } else if ((insn & INSN_MASK_C_FSW) == INSN_MATCH_C_FSW) {
+ len = 4;
+ reg = RVC_RS2S(insn);
+ addr = GET_SP(regs) + RVC_LW_IMM(insn);
+ } else if ((insn & INSN_MASK_C_FSWSP) == INSN_MATCH_C_FSWSP) {
+ len = 4;
+ reg = REG_OFFSET(insn, SH_RS2C);
+ addr = GET_SP(regs) + RVC_SWSP_IMM(insn);
+#endif
+#endif
+ } else {
+ return truly_illegal_insn(insn, regs);
+ }
+
+ val.data_u64 = sbi_float_get_f64(reg);
+
+ if (addr & (len - 1)) {
+ for (i = 0; i < len; i++) {
+ sbi_store_u8((void *)(addr + i), val.data_bytes[i],
+ &uptrap);
+ if (uptrap.cause) break;
+ }
+ } else if (len == 8) {
+ sbi_store_u64((void *)addr, val.data_u64, &uptrap);
+ } else if (len == 4) {
+ sbi_store_u32((void *)addr, val.data_u32, &uptrap);
+ } else {
+ sbi_store_u16((void *)addr, val.data_u16, &uptrap);
+ }
+
+ if (uptrap.cause) {
+ uptrap.epc = regs->mepc;
+ uptrap.tinst = sbi_float_synthesize_tinst(uptrap.cause, insn, len, reg);
+ return sbi_trap_redirect(regs, &uptrap);
+ }
+ regs->mepc += INSN_LEN(insn);
+ sbi_pmu_ctr_incr_fw(SBI_PMU_FW_EMULATE_FLOAT_STORE);
+
+ return 0;
+}
+
+int sbi_float_init(struct sbi_scratch *scratch, bool cold_boot)
+{
+ if (misa_extension('D'))
+ return 0;
+
+ if (cold_boot)
+ float_regs_off = sbi_scratch_alloc_offset(sizeof(struct sbi_float_regs));
+
+ if (!float_regs_off)
+ return SBI_ENOMEM;
+
+ return 0;
+}
diff --git a/lib/sbi/sbi_float_op.c b/lib/sbi/sbi_float_op.c
new file mode 100644
index 0000000..038d8cc
--- /dev/null
+++ b/lib/sbi/sbi_float_op.c
@@ -0,0 +1,460 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * XXX
+ */
+
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/riscv_fp.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_float_ldst.h>
+#include <sbi/sbi_pmu.h>
+#include <sbi/sbi_trap.h>
+
+// todo: wide float return, single-pass fma, shifts->masks, single format
+
+struct decode_fp {
+ u64 sig;
+ u64 bits;
+ u64 cmp;
+ ulong sign;
+ slong exp;
+ ulong iclass;
+};
+
+#define ISNAN 0x801
+#define IQNAN 0x1001
+#define IINF 0x40A
+#define INORM 0x210
+#define IDENORM 0x120
+#define IZERO 0xC4
+
+#define IANYNAN 1
+#define IANYINF 2
+#define IANYZERO 4
+
+#define POS 0x3F0
+#define NEG 0x30F
+
+#define ITOCLASS 3
+
+struct decode_fp decode(ulong in_size, ulong reg, ulong guards)
+{
+ struct decode_fp dt;
+ u64 val = dt.bits = sbi_float_get_f64(reg & 31);
+ ulong sigbit = (23 | 52<<8 | 10<<16) >> 8*in_size & 255;
+ ulong expbit = ( 8 | 11<<8 | 5<<16) >> 8*in_size & 255;
+ ulong allbit = sigbit+expbit+1;
+
+ /* incorrectly boxed -> canonical NaN */
+ if (allbit < 64 && ~val >> allbit) {
+ dt.sign = 0;
+ dt.sig = 3 << 51;
+ dt.exp = 0;
+ dt.iclass = IQNAN;
+ dt.cmp = 0;
+ dt.bits = -1<<(sigbit-1) - 1<<(sigbit+expbit);
+ return dt;
+ }
+
+ dt.sign = val >> sigbit+expbit & 1;
+ val &= (1 << (allbit-1))-1;
+ dt.cmp = val ^ -d.sign;
+ dt.sig = (val & (1 << sigbit)-1) << 52-sigbit;
+ ulong maxexp = (1 << expbit)-1;
+ ulong rawexp = val >> sigbit & maxexp;
+ dt.exp = rawexp - maxexp/2 - 52;
+ if (rawexp == maxexp) {
+ if (dt.sig & (1 << 51)) {
+ dt.iclass = ISNAN;
+ } else if (decoded[i].sig) {
+ dt.iclass = IQNAN;
+ } else {
+ dt.iclass = IINF;
+ }
+ } else {
+ if (rawexp == 0) {
+ if (dt.sig) {
+ dt.iclass = IDENORM;
+ } else {
+ dt.iclass = IZERO;
+ }
+ } else {
+ dt.iclass = INORM;
+ dt.sig |= (1 << 52);
+ }
+ // guard bits inserted here
+ while (dt.sig && !(d.sig >> 55)) {
+ dt.sig <<= 1;
+ dt.exp--;
+ }
+ }
+ return dt;
+}
+
+static u64 do_round2(ulong drm, ulong sign, u64 sig)
+{
+ ulong add;
+ switch (drm) {
+ case RM_RMM: add = 2; break;
+ case RM_RNE: add = (sig & 4) ? 3 : 1; break;
+ case RM_RUP: add = sign ? 0 : 3; break;
+ case RM_RDN: add = sign ? 3 : 0; break;
+ case RM_RTZ: add = 0; break;
+ }
+ return add;
+}
+
+int sbi_float_operate_insn(ulong insn, struct sbi_trap_regs *regs)
+{
+ ulong opcode = insn & 0x7F;
+ ulong funct5 = (opcode == 0x53) ? (insn >> 27) & 31 : 32;
+
+ ulong out_size = (insn >> 25) & 3;
+ if (out_size == PRECISION_Q) goto illegal;
+
+ if (!IS_FP_ON()) goto illegal;
+ ulong csr = sbi_float_get_csr();
+
+ ulong srm = (insn >> 12) & 3;
+ ulong drm = (srm == FRM_DYN) ? (csr >> 5) : srm;
+ if (drm > FRM_RMM) goto illegal;
+
+ ulong in_size = (funct5 == F5_CVT_SIZE) ? ((insn >> 20) & 3) : out_size;
+
+ struct decode_fp d[3];
+
+ ulong utmp;
+ __uint128_t prodsig, addsig;
+
+ switch (funct5) {
+ case F5_ADD:
+ case F5_SUB:
+ snan &= 3;
+ d[1].iclass = INORM;
+ d[1].sig = 1 << 55;
+ d[1].exp = -55;
+ d[1].sign = 0;
+ goto fma;
+ case F5_MUL:
+ snan &= 3;
+ d[2].iclass = IZERO;
+ d[2].sig = 0;
+ d[2].sign = d[0].sign ^ d[1].sign;
+ goto fma;
+ case F5_PSEUDO_FMA:
+ d[0].sign ^= (insn >> 3) & 1;
+ d[2].sign ^= (insn >> 2) & 1;
+fma:
+ if ((d[0].iclass | d[1].iclass) & IANYNAN) goto canonical_nan;
+
+ d[0].sign ^= d[1].sign;
+ if ((d[0].iclass | d[1].iclass) & IANYINF) {
+ if ((d[0].iclass | d[1].iclass) & IANYZERO) goto invalid_nan;
+ if ((d[2].iclass & IANYINF) && d[2].sign != d[0].sign) goto invalid_nan;
+ if (d[2].iclass & IANYNAN) goto canonical_nan;
+ goto infinity;
+ }
+ if (d[2].iclass & IANYNAN) goto canonical_nan;
+ if (d[2].iclass & IANYINF) {
+ d[0].sign = d[2].sign;
+ goto infinity;
+ }
+ prodsig = (__uint128_t)d[0].sig * d[1].sig;
+ d[0].exp += d[1].exp;
+ if (prodsig == 0)
+ d[0].exp = d[2].exp;
+ if (d[2].sig == 0)
+ d[2].exp = d[0].exp;
+ while (d[0].exp + 52 < d[2].exp) {
+ prodsig = (prodsig >> 1) | (prodsig & 1);
+ d[0].exp++;
+ }
+ while (d[2].exp < d[0].exp) {
+ d[2].sig = (d[2].sig >> 1) | (d[2].sig & 1);
+ d[2].exp++;
+ }
+ if (d[2].sign ^ d[0].sign) d[2].sig = -d[2].sig;
+ addsig = (__uint128_t)(s64)d[2].sig;
+ while (d[0].exp < d[2].exp) {
+ d[2].exp--;
+ addsig <<= 1;
+ }
+ prodsig += addsig;
+ while (prodsig != (__uint128_t)(s64)prodsig) {
+ prodsig = (prodsig >> 1) | (prodsig & 1);
+ d[0].exp++;
+ }
+ d[0].sig = prodsig;
+ if (d[0].sig == 0) {
+ if (drm == RM_RDN) {
+ d[0].sign |= d[2].sign;
+ } else {
+ d[0].sign &= d[2].sign;
+ }
+ }
+float_return:
+ if (d[0].iclass & IANYINF) goto infinity;
+ if (d[0].iclass & IANYNAN) goto canonical_nan;
+ if (d[0].sig == 0) {
+ d[0].bits = (-1 << out_bits) | d[0].sign << out_bits-1;
+ goto raw_return;
+ }
+
+ if (d[0].sig >> 63) {
+ d[0].sig = -d[0].sig;
+ d[0].sign = !d[0].sign;
+ }
+ while (!(d[0].sig >> out_sigbit+2)) {
+ d[0].sig <<= 1;
+ d[0].exp--;
+ }
+ while (d[0].sig >> out_sigbit+3) {
+ d[0].sig = d[0].sig>>1 | d[0].sig&1;
+ d[0].exp++;
+ }
+ d[0].exp = d[0].exp + out_sigbit + 2 + out_maxexp>>1;
+ if (d[0].exp > 0) {
+ if (d[0].sig & 3) csr |= FFLAGS_NX;
+ d[0].sig += do_round2(drm, d[0].sign, d[0].sig);
+ d[0].sig >>= 2;
+ if (d[0].sig >> out_sigbit+1) {
+ d[0].sig >>= 1;
+ d[0].exp++;
+ }
+ if (d[0].exp >= out_maxexp) {
+ csr |= (FFLAGS_OV | FFLAGS_NX);
+ if (drm == RM_RTZ || drm == RM_RUP && d[0].sign || drm == RM_RDN && !d[0].sign) {
+ d[0].exp = out_maxexp - 1;
+ d[0].sig = (2 << out_sigbit) - 1;
+ } else {
+infinity:
+ d[0].sig = 0;
+some_nan:
+ d[0].exp = out_maxexp;
+ }
+ }
+ } else {
+ ulong tiny = d[0].exp < 0 || do_round2(drm, d[0].sign, d[0].sig) < 1<<(out_sigbit+1);
+ while (d[0].exp < 1) {
+ d[0].exp++;
+ d[0].sig = d[0].sig>>1 | d[0].sig&1;
+ }
+ d[0].exp = 0;
+ ulong nx = d[0].sig & 3;
+ d[0].sig = do_round2(drm, d[0].sign, d[0].sig);
+ if (d[0].sig >> out_sigbit) {
+ d[0].exp++;
+ if (nx) csr |= FFLAGS_NX;
+ } else {
+ if (nx) csr |= (FFLAGS_NX | FFLAGS_UF);
+ }
+ }
+ d[0].bits = -1<<out_bits | d[0].sign<<(out_bits-1) | d[0].exp<<out_sigbit | d[0].sig&((1<<out_sigbit)-1);
+ goto raw_return;
+invalid_nan:
+ csr |= FFLAGS_NV;
+canonical_nan:
+ d[0].sig = 1 << (out_sigbit-1);
+ goto some_nan;
+ case F5_SQRT:
+ snan &= 1;
+ if (rs2) goto illegal;
+ if (d[0].sign && !(d[0].iclass & (IANYZERO | IANYNAN))) goto invalid_nan;
+ if (d[0].iclass & (IANYINF | IANYZERO)) goto raw_return;
+ if (d[0].iclass & IANYNAN) goto canonical_nan;
+
+ if (d[0].exp & 1) d[0].sig <<= 1; /* exp &= ~1 */
+ d[0].exp >>= 1;
+
+ d[1].sig = d[0].sig;
+ d[0].sig = 0;
+
+ for (utmp = 57; utmp; utmp--) { /* probably not optimal */
+ d[1].sig <<= 1;
+ d[0].sig <<= 1;
+ if (d[1].sig >= (2 * d[0].sig + 1) << utmp) {
+ d[1].sig -= (2 * d[0].sig + 1) << utmp;
+ d[0].sig |= 1;
+ }
+ }
+ d[0].exp -= 29;
+ goto float_return;
+
+ case F5_DIV:
+ snan &= 3;
+ if ((d[0].iclass | d[1].iclass) & IANYNAN) goto canonical_nan;
+ if (d[0].iclass & d[1].iclass & (IANYINF | IANYZERO)) goto invalid_nan;
+ d[0].sign ^= d[1].sign;
+ if (d[0].iclass & IANYINF) goto float_return;
+ if (d[1].iclass & IANYZERO) {
+ csr |= FFLAGS_DZ;
+ goto infinity;
+ }
+ if (d[1].iclass & IANYINF) {
+ d[0].sig = 0;
+ goto float_return;
+ }
+ d[2].sig = d[0].sig;
+ d[0].sig = 0;
+ for (utmp = 55; utmp; utmp--) {
+ if (d[1].sig <= d[2].sig) {
+ d[2].sig -= d[1].sig;
+ d[0].sig++;
+ }
+ d[0].sig <<= 1;
+ d[2].sig <<= 1;
+ }
+ d[0].exp = d[0].exp - d[1].exp - 55;
+ goto float_return;
+
+ case F5_SGNJ:
+ snan = 0;
+ switch (srm) {
+ case 0: utmp = d[0].sign ^ d[1].sign; break;
+ case 1: utmp = 1 ^ d[0].sign ^ d[1].sign; break;
+ case 2: utmp = d[1].sign; break;
+ default: goto illegal;
+ }
+ d[0].bits ^= utmp<<(out_bits-1);
+ goto raw_return;
+ case F5_MINMAX:
+ if (srm > 1) goto illegal;
+ snan &= 3;
+ if (d[0].iclass & d[1].iclass & IANYNAN) goto canonical_nan;
+ if (d[1].iclass & IANYNAN) goto raw_return;
+ if (d[0].iclass & IANYNAN || (d[0].cmp > d[1].cmp) ^ srm) {
+ d[0].bits = d[1].bits;
+ }
+raw_return:
+ sbi_float_set_f64(rd, d[0].bits);
+ break;
+ case F5_CMP:
+ snan &= 3;
+ d[0].cmp -= (d[0].cmp == 0);
+ d[1].cmp -= (d[1].cmp == 0);
+ switch (srm) {
+ case 0: utmp = d[0].cmp <= d[1].cmp; break;
+ case 1: utmp = d[0].cmp < d[1].cmp; break;
+ case 2: utmp = d[0].cmp == d[1].cmp; break;
+ default: goto illegal;
+ }
+ if ((d[0].iclass | d[1].iclass) & IANYNAN) {
+ utmp = 0;
+ if (srm == 0) csr |= FFLAGS_NV;
+ }
+ SET_RD(insn, utmp);
+ break;
+
+
+ case F5_CVT_SIZE:
+ snan &= 1;
+ if (rs2 >> 2 || in_size == out_size) goto illegal;
+ goto float_return;
+ case F5_CVT_TOINT: // rs2 W WU L LU
+ snan &= 1;
+ if (d[0].iclass & IANYNAN) d[0].sign = 0;
+ if (d[0].iclass & (IANYNAN | IANYINF)) {
+ d[0].exp = 1;
+ d[0].sig = -1;
+ }
+ while (d[0].exp < -2) {
+ d[0].sig = (d[0].sig >> 1) | (d[0].sig & 1);
+ d[0].exp++;
+ }
+ while (d[0].exp > 0 || d[0].exp == -1) {
+ if (d[0].sig >> 63) {
+ d[0].sig = -1;
+ csr |= FFLAGS_NV;
+ } else {
+ d[0].sig <<= 1;
+ }
+ d[0].exp--;
+ }
+ low = 0;
+ if (d[0].exp < 0)
+ d[0].sig = do_round2(drm, d[0].sign, d[0].sig);
+ switch (rs2) {
+ case TOFROMINT_W: utmp = (1ULL << 31) - 1; break;
+ case TOFROMINT_WU: utmp = (1ULL << 32) - 1; break;
+#if __riscv_xlen == 64
+ case TOFROMINT_L: utmp = (1ULL << 63) - 1; break;
+ case TOFROMINT_LU: utmp = -1; break;
+#endif
+ default: goto illegal;
+ }
+ if (d[0].sign) utmp = ~utmp;
+ if (d[0].sig > utmp) {
+ d[0].sig = utmp;
+ csr |= FFLAGS_NV;
+ } else if (low) {
+ csr |= FFLAGS_NX;
+ }
+ if (d[0].sign) d[0].sig = -d[0].sig;
+#if __riscv_xlen == 64
+ if (rs2 == TOFROMINT_WU) d[0].sig = (s32)d[0].sig << 32 >> 32;
+#endif
+ SET_RD(insn, d[0].sig);
+ break;
+
+ case F5_CVT_FROMINT:
+ snan = 0;
+ d[0].exp = 0;
+ d[0].iclass = 0;
+ d[0].sign = 0;
+ utmp = GET_RS1(insn, regs);
+ switch (rs2) {
+ case 0:
+ d[0].sig = (s32)utmp;
+ break;
+ case 1:
+ d[0].sig = (u32)utmp;
+ break;
+#if __riscv_xlen == 64
+ case 2:
+ if (((s64)(utmp << 4) >> 4) != utmp)
+ utmp = (s64)utmp >> 2 | utmp & 1;
+ d[0].sig = utmp;
+ break;
+ case 3:
+ if (utmp >> 60)
+ utmp = utmp >> 2 | utmp & 1;
+ d[0].sig = utmp;
+ break;
+#endif
+ default:
+ goto illegal;
+ }
+ goto float_return;
+
+ case F5_MV_TOINT:
+ snan = 0;
+ if (rs2 || srm > 1 || srm == 0 && out_bits > __riscv_xlen) goto illegal;
+ if (srm) {
+ SET_RD(insn, d[0].iclass >> ITOCLASS & (d[0].sign ? NEG : POS));
+ } else {
+ ulong shift = __riscv_xlen - out_bits;
+ SET_RD(insn, (slong)d[0].bits << shift >> shift);
+ }
+ break;
+
+ case F5_MV_FROMINT:
+ snan = 0;
+ if (rs2 || srm) goto illegal;
+ d[0].bits = GET_RS1(insn, regs) | -(1 << out_bits);
+ goto raw_return;
+
+ default:
+illegal:
+ return truly_illegal_insn(insn, regs);
+ }
+
+ if (snan) csr |= FFLAGS_NV;
+ sbi_float_set_csr(csr);
+ regs->mepc += 4;
+ return 0;
+}
diff --git a/lib/sbi/sbi_illegal_insn.c b/lib/sbi/sbi_illegal_insn.c
index 9691bce..c9d4282 100644
--- a/lib/sbi/sbi_illegal_insn.c
+++ b/lib/sbi/sbi_illegal_insn.c
@@ -102,9 +102,15 @@ static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
return 0;
}
+#ifndef CONFIG_FP_EMULATION
+#define sbi_float_load_insn truly_illegal_insn
+#define sbi_float_store_insn truly_illegal_insn
+#define sbi_float_operate_insn truly_illegal_insn
+#endif
+
static const illegal_insn_func illegal_insn_table[32] = {
truly_illegal_insn, /* 0 */
- truly_illegal_insn, /* 1 */
+ sbi_float_load_insn, /* 1 */
truly_illegal_insn, /* 2 */
misc_mem_opcode_insn, /* 3 */
truly_illegal_insn, /* 4 */
@@ -112,18 +118,18 @@ static const illegal_insn_func illegal_insn_table[32] = {
truly_illegal_insn, /* 6 */
truly_illegal_insn, /* 7 */
truly_illegal_insn, /* 8 */
- truly_illegal_insn, /* 9 */
+ sbi_float_store_insn, /* 9 */
truly_illegal_insn, /* 10 */
truly_illegal_insn, /* 11 */
truly_illegal_insn, /* 12 */
truly_illegal_insn, /* 13 */
truly_illegal_insn, /* 14 */
truly_illegal_insn, /* 15 */
- truly_illegal_insn, /* 16 */
- truly_illegal_insn, /* 17 */
- truly_illegal_insn, /* 18 */
- truly_illegal_insn, /* 19 */
- truly_illegal_insn, /* 20 */
+ sbi_float_operate_insn, /* 16 */
+ sbi_float_operate_insn, /* 17 */
+ sbi_float_operate_insn, /* 18 */
+ sbi_float_operate_insn, /* 19 */
+ sbi_float_operate_insn, /* 20 */
truly_illegal_insn, /* 21 */
truly_illegal_insn, /* 22 */
truly_illegal_insn, /* 23 */
diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c
index a8500e5..f96db2e 100644
--- a/lib/sbi/sbi_init.c
+++ b/lib/sbi/sbi_init.c
@@ -302,6 +302,12 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
sbi_hart_hang();
}
+ rc = sbi_float_init(scratch, TRUE);
+ if (rc) {
+ sbi_printf("%s: float init failed (error %d)\n", __func__, rc);
+ sbi_hart_hang();
+ }
+
rc = sbi_ecall_init();
if (rc) {
sbi_printf("%s: ecall init failed (error %d)\n", __func__, rc);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment