@@ -1036,6 +1036,77 @@ float32_math (SIM_CPU *cpu, int rd, int rs1, int rs2,
}
}
+/* Round and return the double value. This function is used before we
+ perform any operation on the input double value. */
+static double
+round_double_input (double value, int rm)
+{
+ double result = value;
+ if (rm == RNE)
+ {
+ double fractional_part = value - trunc (value);
+ /* Check if the number is halfway between two values. */
+ if (fractional_part == 0.5 || fractional_part == -0.5)
+ {
+ result = floor (value);
+ if (fmod (result, 2.0) != 0)
+ result += (value > 0) ? 1.0 : -1.0;
+ }
+ else
+ result = round (value);
+ }
+ else if (rm == RTZ)
+ result = trunc (value);
+ else if (rm == RDN)
+ result = floor (value);
+ else if (rm == RUP)
+ result = ceil (value);
+ else
+ {
+ /* No direct match for RMM. Simulate it. */
+ double fracPart = value - (int64_t) value;
+ if (fracPart > 0.5 || fracPart < -0.5)
+ result = round (value);
+ else if (fracPart == 0.5)
+ result = ceil (value);
+ else if (fracPart == -0.5)
+ result = floor (value);
+ else
+ result = value;
+ }
+ return result;
+}
+
+/* Round and return the double value. This function is used after we
+ perform the operation to get the result rounded. */
+static double
+round_double_output (double value, int rm)
+{
+ double result;
+ if (rm == RNE)
+ result = value;
+ else if (rm == RTZ)
+ result = trunc (value);
+ else if (rm == RDN)
+ result = floor (value);
+ else if (rm == RUP)
+ result = ceil (value);
+ else
+ {
+ /* No direct match for RMM. Simulate it. */
+ double fracPart = value - (int64_t) value;
+ if (fracPart > 0.5 || fracPart < -0.5)
+ result = round (value);
+ else if (fracPart == 0.5)
+ result = ceil (value);
+ else if (fracPart == -0.5)
+ result = floor (value);
+ else
+ result = value;
+ }
+ return result;
+}
+
/* Round and return the float value. This function is used before we
perform any operation on the input float value. */
static float
@@ -1107,6 +1178,183 @@ round_float_output (float value, int rm)
return result;
}
+/* Convert the double precision floating point value into integer. */
+static void
+convert_double_to_int (SIM_CPU *cpu, int rd, int rs1, int sign,
+ int rm, int is_32bit)
+{
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ double src, rounded;
+ uint64_t rs1_bits;
+ int iclamp;
+ uint32_t uclamp;
+ rs1_bits = (uint64_t) riscv_cpu->fpregs[rs1];
+ memcpy (&src, &rs1_bits, sizeof (src));
+
+ if (rm == DYN)
+ rm = riscv_cpu->csr.frm;
+
+ /* Get the input rounded. */
+ rounded = round_double_input (src, rm);
+
+ if (sign == SIGNED)
+ {
+ /* Clamp the input to int32_t range. */
+ iclamp = min (max ((int64_t) rounded, INT_MIN), INT_MAX);
+ if (is_32bit)
+ store_rd (cpu, rd, (int) iclamp);
+ else
+ store_rd (cpu, rd, (int64_t) iclamp);
+ }
+ else
+ {
+ /* Clamp the input to uint32_t range. */
+ uclamp = min (max ((uint64_t) rounded, 0), UINT_MAX);
+ if (rounded < 0.0f)
+ uclamp = 0;
+ if (is_32bit)
+ store_rd (cpu, rd, (uint32_t) uclamp);
+ else
+ store_rd (cpu, rd, (uint64_t) uclamp);
+ }
+}
+
+/* Convert the double precision floating point value into long integer. */
+static void
+convert_double_to_long (SIM_CPU *cpu, int rd, int rs1, int sign,
+ int rm)
+{
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ double src, rounded;
+ uint64_t rs1_bits;
+ int64_t lclamp;
+ uint64_t luclamp;
+ rs1_bits = (uint64_t) riscv_cpu->fpregs[rs1];
+ memcpy (&src, &rs1_bits, sizeof (src));
+
+ if (rm == DYN)
+ rm = riscv_cpu->csr.frm;
+
+ /* Get the input rounded. */
+ rounded = round_double_input (src, rm);
+
+ if (sign == SIGNED)
+ {
+ /* Clamp the input to int64_t range. */
+ lclamp = min (max ((int64_t) rounded, LONG_MIN), LONG_MAX);
+ store_rd (cpu, rd, (int64_t) lclamp);
+ }
+ else
+ {
+ /* Clamp the input to uint64_t range. */
+ luclamp = min (max ((uint64_t) rounded, 0), ULONG_MAX);
+ if (rounded < 0.0f)
+ luclamp = 0;
+ store_rd (cpu, rd, (uint64_t) luclamp);
+ }
+}
+
+/* Convert the long integer value into double precision floating point. */
+static void
+convert_long_to_double (SIM_CPU *cpu, int rd, int rs1, int sign,
+ int rm)
+{
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ uint64_t rd_bits;
+ uint64_t usrc = (uint64_t) riscv_cpu->regs[rs1];
+ int64_t isrc = (int64_t) riscv_cpu->regs[rs1];
+ double result = .0;
+ int old_rm;
+
+ if (rm == DYN)
+ rm = riscv_cpu->csr.frm;
+
+ old_rm = set_riscv_rounding_mode (rm);
+
+ if (rm == RMM)
+ {
+ if (sign == SIGNED)
+ {
+ if (isrc > 0)
+ {
+ fesetround (FE_UPWARD);
+ result = (double) isrc;
+ result = ceil (result);
+ }
+ else
+ {
+ fesetround (FE_DOWNWARD);
+ result = (double) isrc;
+ result = floor (result);
+ }
+ }
+ else
+ {
+ /* Since it's unsigned, it's always positive. */
+ fesetround (FE_UPWARD);
+ result = (double) usrc;
+ result = ceil (result);
+ }
+ }
+ else
+ {
+ if (sign == SIGNED)
+ result = (double) isrc;
+ else
+ result = (double) usrc;
+
+ /* Get the result rounded. */
+ result = round_double_output (result, rm);
+ }
+
+ /* Restore rounding mode. */
+ fesetround (old_rm);
+
+ /* Store the result. */
+ memcpy (&rd_bits, &result, sizeof (result));
+ store_fp (cpu, rd, rd_bits);
+}
+
+/* Convert the double precision floating point value into single precision. */
+static void
+double_to_float (SIM_CPU *cpu, int rd, int rs1, int rm)
+{
+ struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu);
+ double src;
+ float result = 0;
+ int old_rm;
+ uint64_t rs1_bits;
+ uint32_t rd_bits;
+
+ if (rm == DYN)
+ rm = riscv_cpu->csr.frm;
+
+ old_rm = set_riscv_rounding_mode (rm);
+
+ rs1_bits = (uint64_t) riscv_cpu->fpregs[rs1];
+ memcpy (&src, &rs1_bits, sizeof (src));
+
+ result = (float) src;
+
+ if (rm == RMM)
+ {
+ if (is_float_halfway (result))
+ {
+ if (result > 0)
+ result = nextafterf (result, INFINITY);
+ else
+ result = nextafterf (result, -INFINITY);
+ }
+ }
+
+ /* Store result. */
+ memcpy (&rd_bits, &result, sizeof (result));
+ store_fp (cpu, rd, rd_bits);
+
+ /* Restore rounding mode. */
+ fesetround (old_rm);
+}
+
static void
convert_float_to_int (SIM_CPU *cpu, int rd, int rs1, int sign,
int rm, int is_32bit)
@@ -1885,6 +2133,84 @@ execute_d (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op)
case MATCH_FSQRT_D | MASK_RM:
float64_math (cpu, rd, rs1, rs2, 0, rm, FSQRT);
break;
+ case MATCH_FCVT_W_D:
+ case MATCH_FCVT_W_D | MASK_RM:
+ TRACE_INSN (cpu, "fcvt.w.d %s, %s, rm=%d;", rd_name, rs1_name, rm);
+ convert_double_to_int (cpu, rd, rs1, SIGNED, rm, 1);
+ break;
+ case MATCH_FCVT_WU_D:
+ case MATCH_FCVT_WU_D | MASK_RM:
+ TRACE_INSN (cpu, "fcvt.wu.d %s, %s, rm=%d;", rd_name, rs1_name, rm);
+ convert_double_to_int (cpu, rd, rs1, UNSIGNED, rm, 1);
+ break;
+ case MATCH_FCVT_D_W:
+ case MATCH_FCVT_D_W | MASK_RM:
+ {
+ double d;
+ uint64_t rd_bits;
+ int32_t isrc;
+ TRACE_INSN (cpu, "fcvt.d.w %s, %s, rm=%d;", rd_name, rs1_name, rm);
+ isrc = (int32_t) riscv_cpu->regs[rs1];
+ d = (double) isrc;
+ memcpy (&rd_bits, &d, sizeof (d));
+ store_fp (cpu, rd, rd_bits);
+ break;
+ }
+ case MATCH_FCVT_D_WU:
+ case MATCH_FCVT_D_WU | MASK_RM:
+ {
+ double d;
+ uint64_t rd_bits;
+ uint32_t usrc;
+ TRACE_INSN (cpu, "fcvt.d.wu %s, %s, rm=%d;", rd_name, rs1_name, rm);
+ usrc = (uint32_t) riscv_cpu->regs[rs1];
+ d = (double) usrc;
+ memcpy (&rd_bits, &d, sizeof (d));
+ store_fp (cpu, rd, rd_bits);
+ break;
+ }
+ case MATCH_FCVT_L_D:
+ case MATCH_FCVT_L_D | MASK_RM:
+ TRACE_INSN (cpu, "fcvt.l.d %s, %s, rm=%d;", rd_name, rs1_name, rm);
+ RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+ convert_double_to_long (cpu, rd, rs1, SIGNED, rm);
+ break;
+ case MATCH_FCVT_LU_D:
+ case MATCH_FCVT_LU_D | MASK_RM:
+ TRACE_INSN (cpu, "fcvt.lu.d %s, %s, rm=%d;", rd_name, rs1_name, rm);
+ RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+ convert_double_to_long (cpu, rd, rs1, UNSIGNED, rm);
+ break;
+ case MATCH_FCVT_D_L:
+ case MATCH_FCVT_D_L | MASK_RM:
+ TRACE_INSN (cpu, "fcvt.d.l %s, %s, rm=%d;", rd_name, rs1_name, rm);
+ RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+ convert_long_to_double (cpu, rd, rs1, SIGNED, rm);
+ break;
+ case MATCH_FCVT_D_LU:
+ case MATCH_FCVT_D_LU | MASK_RM:
+ TRACE_INSN (cpu, "fcvt.d.lu %s, %s, rm=%d;", rd_name, rs1_name, rm);
+ RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name);
+ convert_long_to_double (cpu, rd, rs1, UNSIGNED, rm);
+ break;
+ case MATCH_FCVT_S_D:
+ case MATCH_FCVT_S_D | MASK_RM:
+ double_to_float (cpu, rd, rs1, rm);
+ break;
+ case MATCH_FCVT_D_S:
+ {
+ float f;
+ double d;
+ uint64_t rd_bits;
+ uint32_t rs1_bits;
+ TRACE_INSN (cpu, "fcvt.d.s %s, %s;", frd_name, frs1_name);
+ rs1_bits = (uint32_t) riscv_cpu->fpregs[rs1];
+ memcpy (&f, &rs1_bits, sizeof (f));
+ d = (double) f;
+ memcpy (&rd_bits, &d, sizeof (d));
+ store_fp (cpu, rd, rd_bits);
+ break;
+ }
default:
TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name);
sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled, SIM_SIGILL);
new file mode 100755
@@ -0,0 +1,111 @@
+# Double precision conversion tests.
+# mach: riscv32 riscv64
+# sim(riscv64): --model RV64ID
+# ld(riscv64): -m elf64lriscv
+# as(riscv64): -march=rv64id
+
+.include "testutils.inc"
+
+ .section .rodata
+ .align 3
+
+_arg1:
+ .double 123.49
+ .word 123
+ .word 0
+ .double -3e9
+ .double 2147483648.5
+ .dword 2147483647
+ .float -12.5
+ .float .0
+ .double -12.5
+
+_result:
+ .word 123
+ .word 0
+ .double 123.0
+ .dword -3000000000
+ .dword 2147483648
+ .double 2147483647
+ .double -12.5
+ .float -12.5
+
+ start
+ .option push
+ .option norelax
+ la a0,_arg1
+ la a1,_result
+ li a2,1
+ .option pop
+
+ # Test fcvt.w.d instruction.
+ fld fa0,0(a0)
+ lw a3,0(a1)
+ fcvt.w.d a5,fa0,rne
+ bne a3,a5,test_fail
+
+ # Test fcvt.wu.d instruction.
+ fld fa0,0(a0)
+ lw a3,0(a1)
+ fcvt.wu.d a5,fa0,rne
+ bne a3,a5,test_fail
+
+ # Test fcvt.d.w instruction.
+ lw a3,8(a0)
+ fld fa0,8(a1)
+ fcvt.d.w fa1,a3
+ feq.d a4,fa0,fa1
+ bne a4,a2,test_fail
+
+ # Test fcvt.d.wu instruction.
+ lw a3,8(a0)
+ fld fa0,8(a1)
+ fcvt.d.wu fa1,a3
+ feq.d a4,fa0,fa1
+ bne a4,a2,test_fail
+
+ # Test fcvt.l.d instruction.
+ fld fa0,16(a0)
+ ld a3,16(a1)
+ fcvt.l.d a5,fa0,rne
+ bne a5,a3,test_fail
+
+ # Test fcvt.lu.d instruction.
+ fld fa0,24(a0)
+ ld a3,24(a1)
+ fcvt.lu.d a5,fa0,rne
+ bne a5,a3,test_fail
+
+ # Test fcvt.d.l instruction.
+ ld a3,32(a0)
+ fld fa0,32(a1)
+ fcvt.d.l fa1,a3,rne
+ feq.d a4,fa0,fa1
+ bne a4,a2,test_fail
+
+ # Test fcvt.d.lu instruction.
+ ld a3,32(a0)
+ fld fa0,32(a1)
+ fcvt.d.lu fa1,a3,rne
+ feq.d a4,fa0,fa1
+ bne a4,a2,test_fail
+
+ # Test fcvt.d.s instruction.
+ flw fa0,40(a0)
+ fld fa1,40(a1)
+ fcvt.d.s fa2,fa0
+ feq.d a4,fa2,fa1
+ bne a4,a2,test_fail
+
+ # Test fcvt.s.d instruction.
+ fld fa0,48(a0)
+ flw fa1,48(a1)
+ fcvt.s.d fa2,fa0
+ feq.s a4,fa2,fa1
+ bne a4,a2,test_fail
+
+test_pass:
+ pass
+
+test_fail:
+ fail