diff --git a/spec/std/isa/inst/F/fdiv.s.yaml b/spec/std/isa/inst/F/fdiv.s.yaml index fd6804438..a25f758dd 100644 --- a/spec/std/isa/inst/F/fdiv.s.yaml +++ b/spec/std/isa/inst/F/fdiv.s.yaml @@ -30,7 +30,10 @@ access: vu: always data_independent_timing: true operation(): | - + check_f_ok($encoding); + RoundingMode mode = rm_to_mode(rm, $encoding); + f[fd] = f32_div(f[fs1], f[fs2], mode); + mark_f_state_dirty(); # SPDX-SnippetBegin # SPDX-FileCopyrightText: 2017-2025 Contributors to the RISCV Sail Model # SPDX-License-Identifier: BSD-2-Clause diff --git a/spec/std/isa/inst/F/fmul.s.yaml b/spec/std/isa/inst/F/fmul.s.yaml index 092fc2e74..388f44e57 100644 --- a/spec/std/isa/inst/F/fmul.s.yaml +++ b/spec/std/isa/inst/F/fmul.s.yaml @@ -30,6 +30,10 @@ access: vu: always data_independent_timing: true operation(): | + check_f_ok($encoding); + RoundingMode mode = rm_to_mode(rm, $encoding); + f[fd] = f32_mul(f[fs1], f[fs2], mode); + mark_f_state_dirty(); # SPDX-SnippetBegin # SPDX-FileCopyrightText: 2017-2025 Contributors to the RISCV Sail Model diff --git a/spec/std/isa/isa/fp.idl b/spec/std/isa/isa/fp.idl index 1dcfe70b9..3e68503c7 100644 --- a/spec/std/isa/isa/fp.idl +++ b/spec/std/isa/isa/fp.idl @@ -313,6 +313,7 @@ function softfloat_shiftRightJam32 { } } + function softfloat_shiftRightJam64 { returns Bits<64> arguments @@ -475,6 +476,19 @@ function softfloat_normSubnormalF16Sig { } } +function softfloat_normSubnormalF32Sig { + returns Bits<8>, Bits<23> + arguments + Bits<32> sp_value + description { + normalize subnormal single-precision value + } + body { + Bits<8> shift_dist = count_leading_zeros<32>(sp_value) - 8; + return 1 - shift_dist, sp_value << shift_dist; + } +} + function softfloat_roundPackToF32 { returns Bits<32> # single precision value arguments @@ -759,7 +773,7 @@ function softfloat_addMagsF32 { U32 sigZ = 0x20000000 + sigA + sigB; if ( sigZ < 0x40000000 ) { - expZ = expZ - 1; + expZ = expZ `- 1'b1; sigZ = sigZ << 1; } } @@ -1236,3 +1250,170 @@ function round_f32_to_integral { return i32_to_f32_no_flag(intermediate, mode); } } + +function f32_mul { + returns U32 + arguments + U32 a, + U32 b, + RoundingMode mode + description { + Returns product of 2 floating point numbers + } + body { + Bits<1> signA = signF32UI(a); + Bits<8> expA = expF32UI(a); + Bits<23> sigA = fracF32UI(a); + Bits<1> signB = signF32UI(b); + Bits<8> expB = expF32UI(b); + Bits<23> sigB = fracF32UI(b); + + # declare a variable to store significand of product + U32 sigZ; + + # declare a variable to store sign of product + Bits<1> signZ; + + # declare a variable to store the exponent part of product + Bits<8> expZ; + + # calculate sign of product + signZ = signA ^ signB; + + U32 magBits; + + if (expA == 0xFF) { + if ((sigA != 0) || ((expB == 0xFF) && (sigB != 0))) { + return softfloat_propagateNaNF32UI(a, b); + magBits = expB | sigB; + if (magBits == 0) { + set_fp_flag(FpFlag::NV); + return UI32_NAN; + } else { + return packToF32UI(signZ, 0xFF, 0); + } + } + } + + if (expB == 0xFF) { + if (sigB != 0) { + return softfloat_propagateNaNF32UI(a, b); + } + magBits = expA | sigA; + if (magBits == 0) { + set_fp_flag(FpFlag::NV); + return UI32_NAN; + } else { + return packToF32UI(signZ, 0xFF, 0); + } + } + + if (expA == 0) { + if (sigA == 0) { + return packToF32UI(signZ, 0, 0); + } + (expA, sigA) = softfloat_normSubnormalF32Sig(sigA); + } + + if (expB == 0) { + if (sigB == 0) { + return packToF32UI(signZ, 0, 0); + } + (expB, sigB) = softfloat_normSubnormalF32Sig(sigB); + } + + expZ = expA + expB - 0x7F; + sigA = (sigA | 0x00800000)<<7; + sigB = (sigB | 0x00800000)<<8; + sigZ = softfloat_shiftRightJam64(sigA `* sigB, 32); + if (sigZ < 0x40000000) { + expZ = expZ `- 1'b1; + sigZ = sigZ << 1; + } + return softfloat_roundPackToF32(signZ, expZ, sigZ, mode); + } +} + +function f32_div { + returns U32 + arguments + U32 a, + U32 b, + RoundingMode mode + description { + Returns quotient of 2 floating point numbers + } + body { + Bits<1> signA = signF32UI(a); + Bits<8> expA = expF32UI(a); + Bits<23> sigA = fracF32UI(a); + Bits<1> signB = signF32UI(b); + Bits<8> expB = expF32UI(b); + Bits<23> sigB = fracF32UI(b); + Bits<1> signZ = signA ^ signB; + Bits<8> expZ; + Bits<23> sigZ; + + U64 sig64A; + + if (expA == 0xFF) { + if (sigA != 0) { + return softfloat_propagateNaNF32UI(a, b); + } + if ((expB == 0xFF) && (sigB != 0)) { + return softfloat_propagateNaNF32UI(a, b); + } + if ((expB == 0xFF) && (sigB == 0)) { + set_fp_flag(FpFlag::NV); + return SP_CANONICAL_NAN; + } + return packToF32UI(signZ, 0xFF, 0); + } + + if (expB == 0xFF) { + if (sigB != 0) { + return softfloat_propagateNaNF32UI(a, b); + } + return packToF32UI(signZ, 0, 0); + } + + if (expB == 0) { + if (sigB == 0) { + if ((expA == 0) && (sigA == 0)) { + set_fp_flag(FpFlag::NV); + return SP_CANONICAL_NAN; + } + set_fp_flag(FpFlag::OF); + set_fp_flag(FpFlag::NX); + return packToF32UI(signZ, 0xFF, 0); + } + (expB, sigB) = softfloat_normSubnormalF32Sig(sigB); + } + if (expA == 0) { + if (sigA == 0) { + return packToF32UI(signZ, 0, 0); + } + (expA, sigA) = softfloat_normSubnormalF32Sig(sigA); + } + + expZ = expA - expB + 0x7E; + sigA = (sigA | 0x800000); + sigB = (sigB | 0x800000); + + if (sigA < sigB) { + expZ = expZ - 1; + sig64A = sigA `<< 31; + } else { + sig64A = sigA `<< 30; + } + + sigZ = sig64A / sigB; + # Check if the lower 6 bits are zero, meaning the division was exact. + # This is used to determine if rounding/jamming is needed. + if (!(sigZ & 0x3F)) { + sigZ = sigZ | ((sigB `* sigZ) != sig64A); + } + + return softfloat_roundPackToF32(signZ, expZ, sigZ, mode); + } +}