zfh32: add semantic functions for  fadd, fsub, fmul, fdiv

PiperOrigin-RevId: 755097965
Change-Id: Iba9282a88a88efef330d905c2004eb8727a51be5
diff --git a/riscv/riscv_zfh.isa b/riscv/riscv_zfh.isa
index 9afe769..f4461f7 100644
--- a/riscv/riscv_zfh.isa
+++ b/riscv/riscv_zfh.isa
@@ -18,7 +18,7 @@
 isa ZFH {
   namespace mpact::sim::riscv::zfh;
   slots {
-    riscv32_zfh_min;
+    riscv32_zfh;
   }
 }
 
@@ -97,15 +97,44 @@
   opcodes {
     flh{(: rs1, I_imm12 : ), (: : frd)},
       resources: {next_pc, rs1 : frd[0..]},
-      semfunc: "&RV64::RiscVILhu", "&RV64::RiscVZfhFlhChild",  // new
+      semfunc: "&RV64::RiscVILhu", "&RV64::RiscVZfhFlhChild",
       disasm: "flh", "%frd, %I_imm12(%rs1)";
     fsh{: rs1, S_imm12, frs2},
       resources: {next_pc, rs1, frs2},
-      semfunc: "&RV64::RiscVISh",  // new
+      semfunc: "&RV64::RiscVISh",
       disasm: "fsh", "%frs2, %S_imm12(%rs1)";
     fmv_xh{: frs1 : rd},
       resources: {next_pc, frs1 : rd[0..]},
-      semfunc: "&RV64::RiscVZfhFMvxh",  // new
+      semfunc: "&RV64::RiscVZfhFMvxh",
       disasm: "fmv.x.h", "%rd, %frs1";
   }
 }
+
+slot riscv32_zfh : riscv32_zfh_min {
+  includes {
+    #include "riscv/riscv_zfh_instructions.h"
+  }
+  default size = 4;
+  default latency = 0;
+  default opcode =
+    disasm: "Unimplemented instruction at 0x%(@:08x)",
+    semfunc: "&RV32VUnimplementedInstruction";
+  opcodes {
+    fadd_h{: frs1, frs2, rm : frd, fflags},
+      resources: {next_pc, frs1, frs2 : frd[0..]},
+      semfunc: "&RiscVZfhFadd",
+      disasm: "fadd.h", "%frd, %frs1, %frs2";
+    fsub_h{: frs1, frs2, rm : frd, fflags},
+      resources: {next_pc, frs1, frs2 : frd[0..]},
+      semfunc: "&RiscVZfhFsub",
+      disasm: "fsub.h", "%frd, %frs1, %frs2";
+    fmul_h{: frs1, frs2, rm : frd, fflags},
+      resources: {next_pc, frs1, frs2 : frd[0..]},
+      semfunc: "&RiscVZfhFmul",
+      disasm: "fmul.h", "%frd, %frs1, %frs2";
+    fdiv_h{: frs1, frs2, rm : frd, fflags},
+      resources: {next_pc, frs1, frs2 : frd[0..]},
+      semfunc: "&RiscVZfhFdiv",
+      disasm: "fdiv.h", "%frd, %frs1, %frs2";
+  }
+}
diff --git a/riscv/riscv_zfh_instructions.cc b/riscv/riscv_zfh_instructions.cc
index cc4d4aa..83e2342 100644
--- a/riscv/riscv_zfh_instructions.cc
+++ b/riscv/riscv_zfh_instructions.cc
@@ -14,6 +14,10 @@
 
 #include "riscv/riscv_zfh_instructions.h"
 
+#include <sys/types.h>
+
+#include <cassert>
+#include <cmath>
 #include <cstdint>
 #include <functional>
 #include <limits>
@@ -27,6 +31,7 @@
 #include "riscv/riscv_csr.h"
 #include "riscv/riscv_fp_host.h"
 #include "riscv/riscv_fp_info.h"
+#include "riscv/riscv_fp_state.h"
 #include "riscv/riscv_instruction_helpers.h"
 #include "riscv/riscv_register.h"
 #include "riscv/riscv_state.h"
@@ -37,6 +42,8 @@
 
 using HalfFP = ::mpact::sim::generic::HalfFP;
 using ::mpact::sim::generic::IsMpactFp;
+using ::mpact::sim::generic::operator*;  // NOLINT: is used below (clang error).
+using ::mpact::sim::generic::IsMpactFp;
 
 namespace {
 
@@ -209,6 +216,76 @@
   return ConvertDoubleToHalfFP(input_value, rm, fflags);
 }
 
+template <typename Argument, typename IntermediateType>
+void RiscVZfhBinaryHelper(
+    const Instruction *instruction,
+    std::function<IntermediateType(IntermediateType, IntermediateType)>
+        operation) {
+  RiscVFPState *rv_fp =
+      static_cast<RiscVState *>(instruction->state())->rv_fp();
+  int rm_value = generic::GetInstructionSource<int>(instruction, 2);
+
+  // If the rounding mode is dynamic, read it from the current state.
+  if (rm_value == *FPRoundingMode::kDynamic) {
+    if (!rv_fp->rounding_mode_valid()) {
+      LOG(ERROR) << "Invalid rounding mode";
+      return;
+    }
+    rm_value = *(rv_fp->GetRoundingMode());
+  }
+  FPRoundingMode rm = static_cast<FPRoundingMode>(rm_value);
+  RiscVCsrDestinationOperand *fflags_dest =
+      static_cast<RiscVCsrDestinationOperand *>(instruction->Destination(1));
+  uint32_t fflags = fflags_dest->GetRiscVCsr()->GetUint32();
+  bool arguments_contain_snan = false;
+  IntermediateType b_emin =
+      std::pow(2.0, 1 - FPTypeInfo<IntermediateType>::kExpBias);
+  IntermediateType result;
+  RiscVBinaryFloatNaNBoxOp<RVFpRegister::ValueType, HalfFP, Argument>(
+      instruction,
+      [&operation, &arguments_contain_snan, &fflags, rv_fp, &rm, &result](
+          Argument a, Argument b) -> HalfFP {
+        IntermediateType a_f;
+        IntermediateType b_f;
+        if (FPTypeInfo<Argument>::IsSNaN(a)) {
+          a_f = absl::bit_cast<IntermediateType>(
+              FPTypeInfo<IntermediateType>::kPosInf | 1);
+          arguments_contain_snan = true;
+        } else {
+          a_f = ConvertFromHalfFP<IntermediateType>(a, fflags);
+        }
+        if (FPTypeInfo<Argument>::IsSNaN(b)) {
+          b_f = absl::bit_cast<IntermediateType>(
+              FPTypeInfo<IntermediateType>::kPosInf | 1);
+          arguments_contain_snan = true;
+        } else {
+          b_f = ConvertFromHalfFP<IntermediateType>(b, fflags);
+        }
+        if (zfh_internal::UseHostFlagsForConversion()) {
+          result = operation(a_f, b_f);
+        } else {
+          ScopedFPStatus set_fpstatus(rv_fp->host_fp_interface(), rm);
+          result = operation(a_f, b_f);
+        }
+        if (!zfh_internal::UseHostFlagsForConversion()) {
+          fflags |= rv_fp->fflags()->GetUint32();
+        }
+        return ConvertToHalfFP(result, rm, fflags);
+      });
+  if (arguments_contain_snan) {
+    fflags_dest->GetRiscVCsr()->SetBits(*FPExceptions::kInvalidOp);
+  }
+  if (!zfh_internal::UseHostFlagsForConversion()) {
+    fflags_dest->GetRiscVCsr()->Write(fflags);
+  }
+  // When the result is less than b_emin before rounding we need to set the
+  // underflow flag.
+  if ((fflags_dest->GetRiscVCsr()->GetUint32() & *FPExceptions::kInexact) &&
+      result != 0 && std::abs(result) < b_emin) {
+    fflags_dest->GetRiscVCsr()->SetBits(*FPExceptions::kUnderflow);
+  }
+}
+
 }  // namespace
 
 namespace RV32 {
@@ -301,6 +378,30 @@
       });
 }
 
+// Add two half precision values. Do the calculation in single precision.
+void RiscVZfhFadd(const Instruction *instruction) {
+  RiscVZfhBinaryHelper<HalfFP, float>(
+      instruction, [](float a, float b) -> float { return a + b; });
+}
+
+// Subtract two half precision values. Do the calculation in single precision.
+void RiscVZfhFsub(const Instruction *instruction) {
+  RiscVZfhBinaryHelper<HalfFP, float>(
+      instruction, [](float a, float b) -> float { return a - b; });
+}
+
+// Multiply two half precision values. Do the calculation in single precision.
+void RiscVZfhFmul(const Instruction *instruction) {
+  RiscVZfhBinaryHelper<HalfFP, float>(
+      instruction, [](float a, float b) -> float { return a * b; });
+}
+
+// Divide two half precision values. Do the calculation in single precision.
+void RiscVZfhFdiv(const Instruction *instruction) {
+  RiscVZfhBinaryHelper<HalfFP, float>(
+      instruction, [](float a, float b) -> float { return a / b; });
+}
+
 // TODO(b/409778536): Factor out generic unimplemented instruction semantic
 //                    function.
 void RV32VUnimplementedInstruction(const Instruction *instruction) {
diff --git a/riscv/riscv_zfh_instructions.h b/riscv/riscv_zfh_instructions.h
index 9181490..fd2f713 100644
--- a/riscv/riscv_zfh_instructions.h
+++ b/riscv/riscv_zfh_instructions.h
@@ -90,6 +90,7 @@
 // TODO(b/409778536): Factor out generic unimplemented instruction semantic
 //                    function.
 void RV32VUnimplementedInstruction(const Instruction *instruction);
+
 HalfFP ConvertSingleToHalfFP(float, FPRoundingMode, uint32_t &);
 HalfFP ConvertDoubleToHalfFP(double, FPRoundingMode, uint32_t &);
 
@@ -97,6 +98,42 @@
 bool UseHostFlagsForConversion();
 }  // namespace zfh_internal
 
+// Source Operands:
+//   frs1: Float Register
+//   frs2: Float Register
+//   rm: Literal Operand (rounding mode)
+// Destination Operands:
+//   frd: Float Register
+//   fflags: Accrued Exception Flags field in FCSR
+void RiscVZfhFadd(const Instruction *instruction);
+
+// Source Operands:
+//   frs1: Float Register
+//   frs2: Float Register
+//   rm: Literal Operand (rounding mode)
+// Destination Operands:
+//   frd: Float Register
+//   fflags: Accrued Exception Flags field in FCSR
+void RiscVZfhFsub(const Instruction *instruction);
+
+// Source Operands:
+//   frs1: Float Register
+//   frs2: Float Register
+//   rm: Literal Operand (rounding mode)
+// Destination Operands:
+//   frd: Float Register
+//   fflags: Accrued Exception Flags field in FCSR
+void RiscVZfhFmul(const Instruction *instruction);
+
+// Source Operands:
+//   frs1: Float Register
+//   frs2: Float Register
+//   rm: Literal Operand (rounding mode)
+// Destination Operands:
+//   frd: Float Register
+//   fflags: Accrued Exception Flags field in FCSR
+void RiscVZfhFdiv(const Instruction *instruction);
+
 }  // namespace riscv
 }  // namespace sim
 }  // namespace mpact
diff --git a/riscv/riscv_zfh_instructions_arm.cc b/riscv/riscv_zfh_instructions_arm.cc
index c8353fd..74104d2 100644
--- a/riscv/riscv_zfh_instructions_arm.cc
+++ b/riscv/riscv_zfh_instructions_arm.cc
@@ -164,17 +164,6 @@
     }
   }
 
-  // Handle flags for the specific underflow case.
-  if (unbounded_half_exponent < 0 ||
-      (unbounded_half_exponent == 0 && fres2 != ftmp)) {
-    fflags |= static_cast<uint32_t>(FPExceptions::kUnderflow);
-  }
-
-  // Handle flags for the specific inexact case.
-  if (fres2 != ftmp) {
-    fflags |= static_cast<uint32_t>(FPExceptions::kInexact);
-  }
-
   // Construct the half float.
   half_fp.value = half_mantissa |
                   (half_exponent << FPTypeInfo<HalfFP>::kSigSize) |
@@ -191,11 +180,17 @@
   T reconstructed_value = ((trailing_significand_float * precision_factor) +
                            implicit_bit_adjustment) *
                           exponent_factor * sign_factor;
+  bool exact_conversion = reconstructed_value == input_value;
 
-  if (reconstructed_value == input_value) {
-    // Clear the flags for exact conversions.
-    fflags &= ~(static_cast<uint32_t>(FPExceptions::kUnderflow) |
-                static_cast<uint32_t>(FPExceptions::kInexact));
+  // Handle flags for the specific underflow case.
+  if (!exact_conversion && (unbounded_half_exponent < 0 ||
+                            (unbounded_half_exponent == 0 && fres2 != ftmp))) {
+    fflags |= static_cast<uint32_t>(FPExceptions::kUnderflow);
+  }
+
+  // Handle flags for the specific inexact case.
+  if (!exact_conversion && (fres2 != ftmp)) {
+    fflags |= static_cast<uint32_t>(FPExceptions::kInexact);
   }
   return half_fp;
 }
diff --git a/riscv/test/riscv_fp_test_base.h b/riscv/test/riscv_fp_test_base.h
index f60d156..f776d16 100644
--- a/riscv/test/riscv_fp_test_base.h
+++ b/riscv/test/riscv_fp_test_base.h
@@ -681,7 +681,8 @@
       for (int rm : {0, 1, 2, 3, 4}) {
         rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
         SetRegisterValues<int, RV32Register>({{kRmName, rm}});
-        SetRegisterValues<R, DestRegisterType>({{kRdName, 0}});
+        SetRegisterValues<DestRegisterType::ValueType, DestRegisterType>(
+            {{kRdName, 0}});
 
         inst->Execute(nullptr);
 
@@ -761,7 +762,8 @@
         rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
         rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
         SetRegisterValues<int, RV32Register>({{kRmName, rm}, {}});
-        SetRegisterValues<R, DestRegisterType>({{kRdName, 0}});
+        SetRegisterValues<DestRegisterType::ValueType, DestRegisterType>(
+            {{kRdName, 0}});
 
         inst->Execute(nullptr);
 
@@ -780,11 +782,13 @@
         FPCompare<R>(op_val, reg_val, delta_position,
                      absl::StrCat(name, "  ", i, ": ",
                                   FloatingPointToString<LHS>(lhs_span[i]), "  ",
-                                  FloatingPointToString<RHS>(rhs_span[i])));
+                                  FloatingPointToString<RHS>(rhs_span[i]),
+                                  " (rm: ", rm, ") "));
         LhsUInt lhs_uint = absl::bit_cast<LhsUInt>(lhs_span[i]);
         RhsUInt rhs_uint = absl::bit_cast<RhsUInt>(rhs_span[i]);
         EXPECT_EQ(test_operation_fflags, instruction_fflags)
-            << std::hex << name << "(" << lhs_uint << ", " << rhs_uint << ")";
+            << std::hex << name << "(" << lhs_uint << ", " << rhs_uint << ")"
+            << " rm: " << rm;
       }
     }
   }
@@ -937,7 +941,8 @@
         rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
         rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
         SetRegisterValues<int, RV32Register>({{kRmName, rm}});
-        SetRegisterValues<R, DestRegisterType>({{kRdName, 0}});
+        SetRegisterValues<DestRegisterType::ValueType, DestRegisterType>(
+            {{kRdName, 0}});
 
         inst->Execute(nullptr);
         // Get the fflags for the instruction execution.
@@ -1376,7 +1381,7 @@
                      FPRoundingMode rm = FPRoundingMode::kRoundToNearest) {
     fflags_ = 0;
     U ret = Convert<U>(rm);
-    fflags = fflags_;
+    fflags |= fflags_;
     return ret;
   }
 
@@ -1454,8 +1459,13 @@
   }
 
   if (FPTypeInfo<FpType>::IsNaN(fp_value_)) {
-    return absl::bit_cast<U>(FPTypeInfo<FpReturnType>::kCanonicalNaN);
-  } else if (FPTypeInfo<FpType>::kPosInf == unsigned_value_) {
+    IntReturnType return_bits =
+        sign() ? 1ULL << (FPTypeInfo<FpReturnType>::kBitSize - 1) : 0;
+    return_bits |= FPTypeInfo<FpReturnType>::kCanonicalNaN;
+    return absl::bit_cast<U>(return_bits);
+  }
+
+  if (FPTypeInfo<FpType>::kPosInf == unsigned_value_) {
     return absl::bit_cast<U>(FPTypeInfo<FpReturnType>::kPosInf);
   } else if (FPTypeInfo<FpType>::kNegInf == unsigned_value_) {
     return absl::bit_cast<U>(FPTypeInfo<FpReturnType>::kNegInf);
@@ -1624,7 +1634,7 @@
 
     if (result != fp_value_double) {
       double b_emin = std::pow(2.0, e_min);
-      if (std::abs(result) <= b_emin || std::abs(fp_value_double) <= b_emin) {
+      if (std::abs(result) < b_emin || std::abs(fp_value_double) < b_emin) {
         fflags_ |= static_cast<uint32_t>(FPExceptions::kUnderflow);
       }
       fflags_ |= static_cast<uint32_t>(FPExceptions::kInexact);
diff --git a/riscv/test/riscv_zfh_instructions_test.cc b/riscv/test/riscv_zfh_instructions_test.cc
index 8b02448..876c5f5 100644
--- a/riscv/test/riscv_zfh_instructions_test.cc
+++ b/riscv/test/riscv_zfh_instructions_test.cc
@@ -33,6 +33,7 @@
 #include "mpact/sim/generic/instruction.h"
 #include "mpact/sim/generic/operand_interface.h"
 #include "mpact/sim/generic/type_helpers.h"
+#include "riscv/riscv_fp_host.h"
 #include "riscv/riscv_fp_info.h"
 #include "riscv/riscv_i_instructions.h"
 #include "riscv/riscv_register.h"
@@ -53,11 +54,16 @@
 using ::mpact::sim::riscv::RiscVZfhCvtHd;
 using ::mpact::sim::riscv::RiscVZfhCvtHs;
 using ::mpact::sim::riscv::RiscVZfhCvtSh;
+using ::mpact::sim::riscv::RiscVZfhFadd;
+using ::mpact::sim::riscv::RiscVZfhFdiv;
 using ::mpact::sim::riscv::RiscVZfhFlhChild;
+using ::mpact::sim::riscv::RiscVZfhFmul;
 using ::mpact::sim::riscv::RiscVZfhFMvhx;
+using ::mpact::sim::riscv::RiscVZfhFsub;
 using ::mpact::sim::riscv::RV32Register;
 using ::mpact::sim::riscv::RV64Register;
 using ::mpact::sim::riscv::RVFpRegister;
+using ::mpact::sim::riscv::RVXRegister;
 using ::mpact::sim::riscv::RV32::RiscVILhu;
 using ::mpact::sim::riscv::RV32::RiscVZfhFMvxh;
 using ::mpact::sim::riscv::test::FpConversionsTestHelper;
@@ -564,6 +570,118 @@
   EXPECT_EQ(absl::bit_cast<uint16_t>(actual_n0), expected_n0);
 }
 
+// Add half precision values. Generate the expected result by using a natively
+// supported float datatype for the operation.
+TEST_F(RV32ZfhInstructionTest, RiscVZfhFadd) {
+  SetSemanticFunction(&RiscVZfhFadd);
+  BinaryOpWithFflagsFPTestHelper<HalfFP, HalfFP, HalfFP>(
+      "fadd.h", instruction_, {"f", "f", "f"}, 32,
+      [this](HalfFP a, HalfFP b) -> std::tuple<HalfFP, uint32_t> {
+        FPRoundingMode rm = rv_fp_->GetRoundingMode();
+        uint32_t fflags = 0;
+        double a_f =
+            FpConversionsTestHelper(a).ConvertWithFlags<double>(fflags);
+        double b_f =
+            FpConversionsTestHelper(b).ConvertWithFlags<double>(fflags);
+        HalfFP result =
+            FpConversionsTestHelper(a_f + b_f).ConvertWithFlags<HalfFP>(fflags,
+                                                                        rm);
+        // inf + -inf, -inf + inf are both invalid.
+        if (std::isinf(a_f) && std::isinf(b_f) && a.value != b.value) {
+          fflags |= static_cast<uint32_t>(
+              mpact::sim::riscv::FPExceptions::kInvalidOp);
+          result.value = FPTypeInfo<HalfFP>::kCanonicalNaN;
+        }
+        return std::make_tuple(result, fflags);
+      });
+}
+
+// Subtract half precision values. Generate the expected result by using a
+// natively supported float datatype for the operation.
+TEST_F(RV32ZfhInstructionTest, RiscVZfhFsub) {
+  SetSemanticFunction(&RiscVZfhFsub);
+  BinaryOpWithFflagsFPTestHelper<HalfFP, HalfFP, HalfFP>(
+      "fsub.h", instruction_, {"f", "f", "f"}, 32,
+      [this](HalfFP a, HalfFP b) -> std::tuple<HalfFP, uint32_t> {
+        FPRoundingMode rm = rv_fp_->GetRoundingMode();
+        uint32_t fflags = 0;
+        double a_f =
+            FpConversionsTestHelper(a).ConvertWithFlags<double>(fflags);
+        double b_f =
+            FpConversionsTestHelper(b).ConvertWithFlags<double>(fflags);
+        HalfFP result =
+            FpConversionsTestHelper(a_f - b_f).ConvertWithFlags<HalfFP>(fflags,
+                                                                        rm);
+        // inf - inf and -inf - -inf are both invalid.
+        if (std::isinf(a_f) && std::isinf(b_f) && a.value == b.value) {
+          fflags |= static_cast<uint32_t>(
+              mpact::sim::riscv::FPExceptions::kInvalidOp);
+          result.value = FPTypeInfo<HalfFP>::kCanonicalNaN;
+        }
+        return std::make_tuple(result, fflags);
+      });
+}
+
+// Multiply half precision values. Generate the expected result by using a
+// natively supported float datatype for the operation.
+TEST_F(RV32ZfhInstructionTest, RiscVZfhFmul) {
+  SetSemanticFunction(&RiscVZfhFmul);
+  BinaryOpWithFflagsFPTestHelper<HalfFP, HalfFP, HalfFP>(
+      "fmul.h", instruction_, {"f", "f", "f"}, 32,
+      [this](HalfFP a, HalfFP b) -> std::tuple<HalfFP, uint32_t> {
+        FPRoundingMode rm = rv_fp_->GetRoundingMode();
+        uint32_t fflags = 0;
+        double a_f =
+            FpConversionsTestHelper(a).ConvertWithFlags<double>(fflags);
+        double b_f =
+            FpConversionsTestHelper(b).ConvertWithFlags<double>(fflags);
+        HalfFP result =
+            FpConversionsTestHelper(a_f * b_f).ConvertWithFlags<HalfFP>(fflags,
+                                                                        rm);
+        // Multiplying infinity and zero is invalid.
+        if ((std::isinf(a_f) && b_f == 0) || (a_f == 0 && std::isinf(b_f))) {
+          fflags |= static_cast<uint32_t>(
+              mpact::sim::riscv::FPExceptions::kInvalidOp);
+          result.value = FPTypeInfo<HalfFP>::kCanonicalNaN;
+        }
+        return std::make_tuple(result, fflags);
+      });
+}
+
+// Divide half precision values. Generate the expected result by using a
+// natively supported float datatype for the operation.
+TEST_F(RV32ZfhInstructionTest, RiscVZfhFdiv) {
+  SetSemanticFunction(&RiscVZfhFdiv);
+  BinaryOpWithFflagsFPTestHelper<HalfFP, HalfFP, HalfFP>(
+      "fdiv.h", instruction_, {"f", "f", "f"}, 32,
+      [this](HalfFP a, HalfFP b) -> std::tuple<HalfFP, uint32_t> {
+        FPRoundingMode rm = rv_fp_->GetRoundingMode();
+        uint32_t fflags = 0;
+        double a_f =
+            FpConversionsTestHelper(a).ConvertWithFlags<double>(fflags);
+        double b_f =
+            FpConversionsTestHelper(b).ConvertWithFlags<double>(fflags);
+        HalfFP result =
+            FpConversionsTestHelper(a_f / b_f).ConvertWithFlags<HalfFP>(fflags,
+                                                                        rm);
+        if ((a_f == 0 && b_f == 0) || (std::isinf(a_f) && std::isinf(b_f))) {
+          // 0 / 0 and inf / inf are both invalid.
+          fflags |= static_cast<uint32_t>(
+              mpact::sim::riscv::FPExceptions::kInvalidOp);
+          result.value = FPTypeInfo<HalfFP>::kCanonicalNaN;
+        } else if (!FPTypeInfo<HalfFP>::IsNaN(a) &&
+                   (b.value == FPTypeInfo<HalfFP>::kPosZero ||
+                    b.value == FPTypeInfo<HalfFP>::kNegZero)) {
+          // Dividing by zero requires an exception for non-NaN dividend values.
+          result.value = ((a.value ^ b.value) & FPTypeInfo<HalfFP>::kNegZero) |
+                         (FPTypeInfo<HalfFP>::kPosInf);
+          fflags |= static_cast<uint32_t>(
+              mpact::sim::riscv::FPExceptions::kDivByZero);
+        }
+        return std::make_tuple(result, fflags);
+      });
+}
+
 class RV64ZfhInstructionTest : public RVZfhInstructionTestBase {};
 
 // Move half precision from a float register to an integer register. The IEEE754
diff --git a/riscv/test/zfh_encoding_test.cc b/riscv/test/zfh_encoding_test.cc
index dd37b8b..56980d3 100644
--- a/riscv/test/zfh_encoding_test.cc
+++ b/riscv/test/zfh_encoding_test.cc
@@ -119,7 +119,7 @@
     uint32_t rs1_adjustment = static_cast<uint32_t>(rs1_index) << rs1_offset;
     enc_->ParseInstruction(base_instruction | rs1_adjustment);
     std::unique_ptr<SourceOperandInterface> src(enc_->GetSource(
-        SlotEnum::kRiscv32ZfhMin, 0, opcode_enum, SourceOpEnum::kRs1, 0));
+        SlotEnum::kRiscv32Zfh, 0, opcode_enum, SourceOpEnum::kRs1, 0));
 
     // Pull the value from the source operand and compare it to the expected
     // value.
@@ -148,7 +148,7 @@
     enc_->ParseInstruction(base_instruction | frd_adjustment);
     std::unique_ptr<RegisterDestinationOperand<RVFpRegister>> dst(
         static_cast<RegisterDestinationOperand<RVFpRegister> *>(
-            enc_->GetDestination(SlotEnum::kRiscv32ZfhMin, 0, opcode_enum,
+            enc_->GetDestination(SlotEnum::kRiscv32Zfh, 0, opcode_enum,
                                  DestOpEnum::kFrd, 0, 0)));
 
     // Pull the value from the destination operand and compare it to the
@@ -179,7 +179,7 @@
     // Parse the instruction and get the source operand.
     enc_->ParseInstruction(base_instruction | src_adjustment);
     std::unique_ptr<SourceOperandInterface> src(enc_->GetSource(
-        SlotEnum::kRiscv32ZfhMin, 0, opcode_enum, source_op_enum, 0));
+        SlotEnum::kRiscv32Zfh, 0, opcode_enum, source_op_enum, 0));
 
     // Pull the value from the source operand and compare it to the expected
     // value.
@@ -205,9 +205,8 @@
   for (int rm = 0; rm <= 6; ++rm) {
     uint32_t rm_adjustment = rm << 12;
     enc_->ParseInstruction(kFcvtSh | rm_adjustment);
-    std::unique_ptr<SourceOperandInterface> src(
-        enc_->GetSource(SlotEnum::kRiscv32ZfhMin, 0, OpcodeEnum::kFcvtSh,
-                        SourceOpEnum::kRm, 0));
+    std::unique_ptr<SourceOperandInterface> src(enc_->GetSource(
+        SlotEnum::kRiscv32Zfh, 0, OpcodeEnum::kFcvtSh, SourceOpEnum::kRm, 0));
     EXPECT_EQ(src->AsUint32(0), rm);
   }
 }
@@ -251,7 +250,7 @@
 
 TEST_F(ZfhEncodingTest, Flh) {
   enc_->ParseInstruction(kFlh);
-  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFlh);
+  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Zfh, 0), OpcodeEnum::kFlh);
 }
 
 TEST_F(ZfhEncodingTest, Flh_imm12) {
@@ -264,7 +263,7 @@
     enc_->ParseInstruction(kFlh | imm_adjustment);
     std::unique_ptr<ImmediateOperand<int32_t>> src(
         static_cast<ImmediateOperand<int32_t> *>(
-            enc_->GetSource(SlotEnum::kRiscv32ZfhMin, 0, OpcodeEnum::kFlh,
+            enc_->GetSource(SlotEnum::kRiscv32Zfh, 0, OpcodeEnum::kFlh,
                             SourceOpEnum::kIImm12, 0)));
     EXPECT_EQ(src->AsInt32(0), expected_imm);
   }
@@ -276,7 +275,7 @@
 
 TEST_F(ZfhEncodingTest, Fsh) {
   enc_->ParseInstruction(kFsh);
-  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFsh);
+  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Zfh, 0), OpcodeEnum::kFsh);
 }
 
 TEST_F(ZfhEncodingTest, Fsh_imm12) {
@@ -290,7 +289,7 @@
     enc_->ParseInstruction(kFsh | imm_adjustment);
     std::unique_ptr<ImmediateOperand<int32_t>> src(
         static_cast<ImmediateOperand<int32_t> *>(
-            enc_->GetSource(SlotEnum::kRiscv32ZfhMin, 0, OpcodeEnum::kFsh,
+            enc_->GetSource(SlotEnum::kRiscv32Zfh, 0, OpcodeEnum::kFsh,
                             SourceOpEnum::kSImm12, 0)));
     EXPECT_EQ(src->AsInt32(0), expected_imm);
   }
@@ -302,7 +301,7 @@
 
 TEST_F(ZfhEncodingTest, FmvXh) {
   enc_->ParseInstruction(kFmvXh);
-  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFmvXh);
+  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Zfh, 0), OpcodeEnum::kFmvXh);
 }
 
 TEST_F(ZfhEncodingTest, FmvXh_frs1) {
@@ -320,8 +319,8 @@
     enc_->ParseInstruction(kFmvXh | rd_adjustment);
     std::unique_ptr<RegisterDestinationOperand<RVXRegister>> dst(
         static_cast<RegisterDestinationOperand<RVXRegister> *>(
-            enc_->GetDestination(SlotEnum::kRiscv32ZfhMin, 0,
-                                 OpcodeEnum::kFmvXh, DestOpEnum::kRd, 0, 0)));
+            enc_->GetDestination(SlotEnum::kRiscv32Zfh, 0, OpcodeEnum::kFmvXh,
+                                 DestOpEnum::kRd, 0, 0)));
     uint32_t observed_value =
         dst->GetRegister()->data_buffer()->Get<uint32_t>(0);
     EXPECT_EQ(observed_value, expected_value);
@@ -330,7 +329,7 @@
 
 TEST_F(ZfhEncodingTest, FmvHx) {
   enc_->ParseInstruction(kFmvHx);
-  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFmvHx);
+  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Zfh, 0), OpcodeEnum::kFmvHx);
 }
 
 TEST_F(ZfhEncodingTest, FmvHx_rs1) {
@@ -343,7 +342,7 @@
 
 TEST_F(ZfhEncodingTest, FcvtSh) {
   enc_->ParseInstruction(kFcvtSh);
-  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFcvtSh);
+  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Zfh, 0), OpcodeEnum::kFcvtSh);
 }
 
 TEST_F(ZfhEncodingTest, FcvtSh_frs1) {
@@ -360,7 +359,7 @@
 
 TEST_F(ZfhEncodingTest, FcvtHs) {
   enc_->ParseInstruction(kFcvtHs);
-  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFcvtHs);
+  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Zfh, 0), OpcodeEnum::kFcvtHs);
 }
 
 TEST_F(ZfhEncodingTest, FcvtHs_frs1) {
@@ -377,7 +376,7 @@
 
 TEST_F(ZfhEncodingTest, FcvtDh) {
   enc_->ParseInstruction(kFcvtDh);
-  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFcvtDh);
+  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Zfh, 0), OpcodeEnum::kFcvtDh);
 }
 
 TEST_F(ZfhEncodingTest, FcvtDh_frs1) {
@@ -394,7 +393,7 @@
 
 TEST_F(ZfhEncodingTest, FcvtHd) {
   enc_->ParseInstruction(kFcvtHd);
-  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFcvtHd);
+  EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32Zfh, 0), OpcodeEnum::kFcvtHd);
 }
 
 TEST_F(ZfhEncodingTest, FcvtHd_frs1) {