From a00aeeabe672e04777faf5680ca100f5353ff323 Mon Sep 17 00:00:00 2001 From: Kuan-Wei Chiu Date: Sat, 6 Jan 2024 22:56:44 +0800 Subject: [PATCH] Fix rounding mode selection Some F extension instructions include an rm field, indicating the rounding mode to be used. When an instruction has an rm field, the rounding mode should be chosen based on the value of that field. If the rm field is set to 0b111, it signifies dynamic selection of the rounding mode based on the fcsr's frm field. The current implementation always selected the rounding mode dynamically based on fcsr's frm, regardless of the rm field in the instruction. This patch corrects this error, ensuring that the rounding mode is now chosen according to the rm field when present. --- src/decode.c | 11 +++---- src/decode.h | 14 +++++++++ src/rv32_template.c | 71 +++++++++++++++++++++++++++++++++++---------- src/softfloat.h | 46 +++++++++++++++++++++++++---- 4 files changed, 115 insertions(+), 27 deletions(-) diff --git a/src/decode.c b/src/decode.c index 2003e6f8..8fc8d13e 100644 --- a/src/decode.c +++ b/src/decode.c @@ -381,6 +381,7 @@ static inline void decode_r4type(rv_insn_t *ir, const uint32_t insn) ir->rs1 = decode_rs1(insn); ir->rs2 = decode_rs2(insn); ir->rs3 = decode_r4type_rs3(insn); + ir->rm = decode_funct3(insn); } #endif @@ -1044,7 +1045,7 @@ static inline bool op_op_fp(rv_insn_t *ir, const uint32_t insn) */ /* decode R-type */ - uint8_t funct3 = decode_funct3(insn); + ir->rm = decode_funct3(insn); decode_rtype(ir, insn); /* dispatch from funct7 field */ @@ -1066,7 +1067,7 @@ static inline bool op_op_fp(rv_insn_t *ir, const uint32_t insn) break; case 0b0010000: /* dispatch from rm region */ - switch (funct3) { + switch (ir->rm) { case 0b000: /* FSGNJ.S */ ir->opcode = rv_insn_fsgnjs; break; @@ -1093,7 +1094,7 @@ static inline bool op_op_fp(rv_insn_t *ir, const uint32_t insn) break; case 0b0010100: /* dispatch from rm region */ - switch (funct3) { + switch (ir->rm) { case 0b000: /* FMIN.S */ ir->opcode = rv_insn_fmins; break; @@ -1106,7 +1107,7 @@ static inline bool op_op_fp(rv_insn_t *ir, const uint32_t insn) break; case 0b1110000: /* dispatch from rm region */ - switch (funct3) { + switch (ir->rm) { case 0b000: /* FMV.X.W */ ir->opcode = rv_insn_fmvxw; break; @@ -1119,7 +1120,7 @@ static inline bool op_op_fp(rv_insn_t *ir, const uint32_t insn) break; case 0b1010000: /* dispatch from rm region */ - switch (funct3) { + switch (ir->rm) { case 0b010: /* FEQ.S */ ir->opcode = rv_insn_feqs; break; diff --git a/src/decode.h b/src/decode.h index 11458cf4..295e2e10 100644 --- a/src/decode.h +++ b/src/decode.h @@ -288,6 +288,20 @@ typedef struct rv_insn { #if RV32_HAS(EXT_C) uint8_t shamt; #endif + +#if RV32_HAS(EXT_F) + /* Floating-point operations use either a static rounding mode encoded in + * the instruction, or a dynamic rounding mode held in frm. A value of 111 + * in the instruction’s rm field selects the dynamic rounding mode held in + * frm. If frm is set to an invalid value (101–111), any subsequent attempt + * to execute a floating-point operation with a dynamic rounding mode will + * cause an illegal instruction trap. Some instructions that have the rm + * field are nevertheless unaffected by the rounding mode; they should have + * their rm field set to RNE (000). + */ + uint8_t rm; +#endif + /* fuse operation */ int32_t imm2; opcode_fuse_t *fuse; diff --git a/src/rv32_template.c b/src/rv32_template.c index c8967fb2..ee2fbcc4 100644 --- a/src/rv32_template.c +++ b/src/rv32_template.c @@ -1413,7 +1413,10 @@ RVOP( RVOP( fmadds, { - set_rounding_mode(rv); + if (likely(ir->rm == 0b111)) + set_dynamic_rounding_mode(rv); + else + set_static_rounding_mode(ir->rm); rv->F[ir->rd] = f32_mulAdd(rv->F[ir->rs1], rv->F[ir->rs2], rv->F[ir->rs3]); set_fflag(rv); @@ -1426,7 +1429,10 @@ RVOP( RVOP( fmsubs, { - set_rounding_mode(rv); + if (likely(ir->rm == 0b111)) + set_dynamic_rounding_mode(rv); + else + set_static_rounding_mode(ir->rm); riscv_float_t tmp = rv->F[ir->rs3]; tmp.v ^= FMASK_SIGN; rv->F[ir->rd] = f32_mulAdd(rv->F[ir->rs1], rv->F[ir->rs2], tmp); @@ -1440,7 +1446,10 @@ RVOP( RVOP( fnmsubs, { - set_rounding_mode(rv); + if (likely(ir->rm == 0b111)) + set_dynamic_rounding_mode(rv); + else + set_static_rounding_mode(ir->rm); riscv_float_t tmp = rv->F[ir->rs1]; tmp.v ^= FMASK_SIGN; rv->F[ir->rd] = f32_mulAdd(tmp, rv->F[ir->rs2], rv->F[ir->rs3]); @@ -1454,7 +1463,10 @@ RVOP( RVOP( fnmadds, { - set_rounding_mode(rv); + if (likely(ir->rm == 0b111)) + set_dynamic_rounding_mode(rv); + else + set_static_rounding_mode(ir->rm); riscv_float_t tmp1 = rv->F[ir->rs1]; riscv_float_t tmp2 = rv->F[ir->rs3]; tmp1.v ^= FMASK_SIGN; @@ -1470,7 +1482,10 @@ RVOP( RVOP( fadds, { - set_rounding_mode(rv); + if (likely(ir->rm == 0b111)) + set_dynamic_rounding_mode(rv); + else + set_static_rounding_mode(ir->rm); rv->F[ir->rd] = f32_add(rv->F[ir->rs1], rv->F[ir->rs2]); set_fflag(rv); }, @@ -1482,7 +1497,10 @@ RVOP( RVOP( fsubs, { - set_rounding_mode(rv); + if (likely(ir->rm == 0b111)) + set_dynamic_rounding_mode(rv); + else + set_static_rounding_mode(ir->rm); rv->F[ir->rd] = f32_sub(rv->F[ir->rs1], rv->F[ir->rs2]); set_fflag(rv); }, @@ -1494,7 +1512,10 @@ RVOP( RVOP( fmuls, { - set_rounding_mode(rv); + if (likely(ir->rm == 0b111)) + set_dynamic_rounding_mode(rv); + else + set_static_rounding_mode(ir->rm); rv->F[ir->rd] = f32_mul(rv->F[ir->rs1], rv->F[ir->rs2]); set_fflag(rv); }, @@ -1506,7 +1527,10 @@ RVOP( RVOP( fdivs, { - set_rounding_mode(rv); + if (likely(ir->rm == 0b111)) + set_dynamic_rounding_mode(rv); + else + set_static_rounding_mode(ir->rm); rv->F[ir->rd] = f32_div(rv->F[ir->rs1], rv->F[ir->rs2]); set_fflag(rv); }, @@ -1518,7 +1542,10 @@ RVOP( RVOP( fsqrts, { - set_rounding_mode(rv); + if (likely(ir->rm == 0b111)) + set_dynamic_rounding_mode(rv); + else + set_static_rounding_mode(ir->rm); rv->F[ir->rd] = f32_sqrt(rv->F[ir->rs1]); set_fflag(rv); }, @@ -1611,7 +1638,10 @@ RVOP( RVOP( fcvtws, { - set_rounding_mode(rv); + if (likely(ir->rm == 0b111)) + set_dynamic_rounding_mode(rv); + else + set_static_rounding_mode(ir->rm); uint32_t ret = f32_to_i32(rv->F[ir->rs1], softfloat_roundingMode, true); if (ir->rd) rv->X[ir->rd] = ret; @@ -1625,7 +1655,10 @@ RVOP( RVOP( fcvtwus, { - set_rounding_mode(rv); + if (likely(ir->rm == 0b111)) + set_dynamic_rounding_mode(rv); + else + set_static_rounding_mode(ir->rm); uint32_t ret = f32_to_ui32(rv->F[ir->rs1], softfloat_roundingMode, true); if (ir->rd) @@ -1653,7 +1686,7 @@ RVOP( RVOP( feqs, { - set_rounding_mode(rv); + set_dynamic_rounding_mode(rv); uint32_t ret = f32_eq(rv->F[ir->rs1], rv->F[ir->rs2]); if (ir->rd) rv->X[ir->rd] = ret; @@ -1670,7 +1703,7 @@ RVOP( RVOP( flts, { - set_rounding_mode(rv); + set_dynamic_rounding_mode(rv); uint32_t ret = f32_lt(rv->F[ir->rs1], rv->F[ir->rs2]); if (ir->rd) rv->X[ir->rd] = ret; @@ -1683,7 +1716,7 @@ RVOP( RVOP( fles, { - set_rounding_mode(rv); + set_dynamic_rounding_mode(rv); uint32_t ret = f32_le(rv->F[ir->rs1], rv->F[ir->rs2]); if (ir->rd) rv->X[ir->rd] = ret; @@ -1708,7 +1741,10 @@ RVOP( RVOP( fcvtsw, { - set_rounding_mode(rv); + if (likely(ir->rm == 0b111)) + set_dynamic_rounding_mode(rv); + else + set_static_rounding_mode(ir->rm); rv->F[ir->rd] = i32_to_f32(rv->X[ir->rs1]); set_fflag(rv); }, @@ -1720,7 +1756,10 @@ RVOP( RVOP( fcvtswu, { - set_rounding_mode(rv); + if (likely(ir->rm == 0b111)) + set_dynamic_rounding_mode(rv); + else + set_static_rounding_mode(ir->rm); rv->F[ir->rd] = ui32_to_f32(rv->X[ir->rs1]); set_fflag(rv); }, diff --git a/src/softfloat.h b/src/softfloat.h index dfc60e4a..415702ea 100644 --- a/src/softfloat.h +++ b/src/softfloat.h @@ -101,17 +101,51 @@ static inline void set_fflag(riscv_t *rv) softfloat_exceptionFlags = 0; } -static inline void set_rounding_mode(riscv_t *rv) +static inline void set_dynamic_rounding_mode(riscv_t *rv) { uint32_t frm = (rv->csr_fcsr >> 5) & (~(1 << 3)); - if (frm == 0b000) + switch (frm) { + case 0b000: softfloat_roundingMode = softfloat_round_near_even; - if (frm == 0b001) + break; + case 0b001: softfloat_roundingMode = softfloat_round_minMag; - if (frm == 0b010) + break; + case 0b010: softfloat_roundingMode = softfloat_round_min; - if (frm == 0b011) + break; + case 0b011: softfloat_roundingMode = softfloat_round_max; - if (frm == 0b100) + break; + case 0b100: softfloat_roundingMode = softfloat_round_near_maxMag; + break; + default: + __UNREACHABLE; + break; + } +} + +static inline void set_static_rounding_mode(uint8_t rm) +{ + switch (rm) { + case 0b000: + softfloat_roundingMode = softfloat_round_near_even; + break; + case 0b001: + softfloat_roundingMode = softfloat_round_minMag; + break; + case 0b010: + softfloat_roundingMode = softfloat_round_min; + break; + case 0b011: + softfloat_roundingMode = softfloat_round_max; + break; + case 0b100: + softfloat_roundingMode = softfloat_round_near_maxMag; + break; + default: + __UNREACHABLE; + break; + } }