Created
September 7, 2025 19:29
-
-
Save sorear/45b6093194f3822e1529a18dcfd41a70 to your computer and use it in GitHub Desktop.
Abandoned OpenSBI F/D/Zfh emulator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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