Binary decoder for 32 bit riscv Zfhmin PiperOrigin-RevId: 752427121 Change-Id: I3d854d8e3f57e16157707b1acf26bb538a20df03
diff --git a/riscv/BUILD b/riscv/BUILD index 4983ea6..cfc03dc 100644 --- a/riscv/BUILD +++ b/riscv/BUILD
@@ -697,6 +697,32 @@ ], ) +mpact_isa_decoder( + name = "zfh_isa", + src = "riscv_zfh.isa", + includes = [], + isa_name = "ZFH", + prefix = "zfh", + deps = [ + ":riscv_g", + ":riscv_zfh_instructions", + "@com_google_absl//absl/functional:bind_front", + ], +) + +mpact_bin_fmt_decoder( + name = "zfh_bin_fmt", + src = "riscv_zfh.bin_fmt", + decoder_name = "ZFH", + includes = [ + "riscv32g.bin_fmt", + ], + prefix = "zfh", + deps = [ + ":zfh_isa", + ], +) + cc_library( name = "riscv32g_decoder", srcs = [
diff --git a/riscv/riscv_zfh.bin_fmt b/riscv/riscv_zfh.bin_fmt new file mode 100644 index 0000000..f0b48ff --- /dev/null +++ b/riscv/riscv_zfh.bin_fmt
@@ -0,0 +1,35 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "riscv/riscv32g.bin_fmt" + +decoder ZFH { + namespace mpact::sim::riscv::zfh; + opcode_enum = "OpcodeEnum"; + includes { + #include "riscv/zfh_decoder.h" + } + RiscVZfhInst32 = { RiscVZfhminInst32 }; +} + +instruction group RiscVZfhminInst32[32] : Inst32Format { + flh : IType : func3 == 0b001, opcode == 0b000'0111; + fsh : SType : func3 == 0b001, opcode == 0b010'0111; + fmv_xh : RType : func7 == 0b1110010, rs2 == 0, func3 == 0b000, opcode == 0b1010011; + fmv_hx : RType : func7 == 0b1111010, rs2 == 0, func3 == 0b000, opcode == 0b1010011; + fcvt_sh : RType : func7 == 0b0100000, rs2 == 0b00010, opcode == 0b1010011; + fcvt_hs : RType : func7 == 0b0100010, rs2 == 0b00000, opcode == 0b1010011; + fcvt_dh : RType : func7 == 0b0100001, rs2 == 0b00010, opcode == 0b1010011; + fcvt_hd : RType : func7 == 0b0100010, rs2 == 0b00001, opcode == 0b1010011; +};
diff --git a/riscv/test/BUILD b/riscv/test/BUILD index da3f7e6..fd65642 100644 --- a/riscv/test/BUILD +++ b/riscv/test/BUILD
@@ -378,6 +378,50 @@ ], ) +cc_library( + name = "zfh_decoder", + testonly = True, + srcs = [ + "zfh_encoding.cc", + ], + hdrs = [ + "riscv_getters_zfh.h", + "zfh_encoding.h", + ], + copts = ["-O3"], + deps = [ + "//riscv:riscv_encoding_common", + "//riscv:riscv_getters", + "//riscv:riscv_state", + "//riscv:zfh_bin_fmt", + "//riscv:zfh_isa", + "@com_google_absl//absl/log", + "@com_google_absl//absl/strings", + "@com_google_mpact-sim//mpact/sim/generic:core", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + ], +) + +cc_test( + name = "zfh_encoding_test", + size = "small", + srcs = [ + "zfh_encoding_test.cc", + ], + deps = [ + ":zfh_decoder", + "//riscv:riscv_state", + "//riscv:zfh_isa", + "@com_google_absl//absl/random", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + "@com_google_mpact-sim//mpact/sim/generic:arch_state", + "@com_google_mpact-sim//mpact/sim/generic:core", + "@com_google_mpact-sim//mpact/sim/generic:type_helpers", + "@com_google_mpact-sim//mpact/sim/util/memory", + ], +) + cc_test( name = "riscv32_htif_semihost_test", size = "small",
diff --git a/riscv/test/riscv_getters_zfh.h b/riscv/test/riscv_getters_zfh.h new file mode 100644 index 0000000..bdb926c --- /dev/null +++ b/riscv/test/riscv_getters_zfh.h
@@ -0,0 +1,129 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_MPACT_RISCV_TEST_RISCV_GETTERS_ZFH_H_ +#define THIRD_PARTY_MPACT_RISCV_TEST_RISCV_GETTERS_ZFH_H_ + +#include <cstdint> +#include <string> + +#include "absl/strings/str_cat.h" +#include "mpact/sim/generic/immediate_operand.h" +#include "mpact/sim/generic/literal_operand.h" +#include "mpact/sim/generic/type_helpers.h" +#include "riscv/riscv_encoding_common.h" +#include "riscv/riscv_getter_helpers.h" +#include "riscv/riscv_getters_vector.h" +#include "riscv/riscv_register.h" +#include "riscv/riscv_register_aliases.h" + +namespace mpact { +namespace sim { +namespace riscv { + +using ::mpact::sim::generic::operator*; // NOLINT: is used below (clang error). + +template <typename Enum, typename Extractors, typename IntegerRegister> +void AddRiscVZfhSourceScalarGetters(SourceOpGetterMap &getter_map, + RiscVEncodingCommon *common) { + // Source operand getters. + Insert(getter_map, *Enum::kRs1, [common]() -> SourceOperandInterface * { + int num = Extractors::Inst32Format::ExtractRs1(common->inst_word()); + if (num == 0) return new generic::IntLiteralOperand<0>({1}); + return GetRegisterSourceOp<IntegerRegister>( + common->state(), absl::StrCat(RiscVState::kXregPrefix, num), + kXRegisterAliases[num]); + }); + // I in IImm12 stands for IType instructions. + Insert(getter_map, *Enum::kIImm12, [common]() -> SourceOperandInterface * { + const auto num = Extractors::IType::ExtractImm12(common->inst_word()); + return new generic::ImmediateOperand<int32_t>(num); + }); + // S in SImm12 stands for SType instructions. + Insert(getter_map, *Enum::kSImm12, [common]() -> SourceOperandInterface * { + const auto num = Extractors::SType::ExtractSImm(common->inst_word()); + return new generic::ImmediateOperand<int32_t>(num); + }); + Insert(getter_map, *Enum::kRm, [common]() -> SourceOperandInterface * { + const auto num = + Extractors::Inst32Format::ExtractFunc3(common->inst_word()); + return new generic::ImmediateOperand<uint8_t>(num); + }); +} + +template <typename Enum, typename Extractors, typename FloatRegister> +void AddRiscVZfhSourceFloatGetters(SourceOpGetterMap &getter_map, + RiscVEncodingCommon *common) { + // Source operand getters. + Insert(getter_map, *Enum::kFrs1, [common]() -> SourceOperandInterface * { + int num = Extractors::Inst32Format::ExtractRs1(common->inst_word()); + return GetRegisterSourceOp<FloatRegister>( + common->state(), absl::StrCat(RiscVState::kFregPrefix, num), + kFRegisterAliases[num]); + }); + Insert(getter_map, *Enum::kFrs2, [common]() -> SourceOperandInterface * { + int num = Extractors::Inst32Format::ExtractRs2(common->inst_word()); + return GetRegisterSourceOp<FloatRegister>( + common->state(), absl::StrCat(RiscVState::kFregPrefix, num), + kFRegisterAliases[num]); + }); +} + +template <typename Enum, typename Extractors, typename IntegerRegister> +void AddRiscVZfhDestScalarGetters(DestOpGetterMap &getter_map, + RiscVEncodingCommon *common) { + // Destination operand getters. + Insert(getter_map, *Enum::kRd, + [common](int latency) -> DestinationOperandInterface * { + auto num = Extractors::Inst32Format::ExtractRd(common->inst_word()); + std::string name = absl::StrCat(RiscVState::kXregPrefix, num); + return mpact::sim::riscv::GetRegisterDestinationOp<IntegerRegister>( + common->state(), name, latency); + }); + Insert(getter_map, *Enum::kFflags, + [common](int latency) -> DestinationOperandInterface * { + return GetCSRSetBitsDestinationOp<uint32_t>(common->state(), + "fflags", latency, ""); + }); +} + +template <typename Enum, typename Extractors, typename FloatRegister> +void AddRiscVZfhDestFloatGetters(DestOpGetterMap &getter_map, + RiscVEncodingCommon *common) { + // Destination operand getters. + Insert(getter_map, *Enum::kFrd, + [common](int latency) -> DestinationOperandInterface * { + auto num = Extractors::Inst32Format::ExtractRd(common->inst_word()); + std::string name = absl::StrCat(RiscVState::kFregPrefix, num); + return mpact::sim::riscv::GetRegisterDestinationOp<FloatRegister>( + common->state(), name, latency); + }); +} + +// This function is used to add the simple resource getters to the given +// "getter map". The function is templated on the enum type that defines the +// simple resource types, the Extractors type that defines the bit +// extraction functions, and the IntRegister and FpRegister types that are used +// to construct the register operand. +template <typename Enum, typename Extractors> +void AddRiscVZfhSimpleResourceGetters(SimpleResourceGetterMap &getter_map, + RiscVEncodingCommon *common) { + // TODO(julianmb): Add resource getters when appropriate. +} + +} // namespace riscv +} // namespace sim +} // namespace mpact + +#endif // THIRD_PARTY_MPACT_RISCV_TEST_RISCV_GETTERS_ZFH_H_
diff --git a/riscv/test/zfh_encoding.cc b/riscv/test/zfh_encoding.cc new file mode 100644 index 0000000..7be9df3 --- /dev/null +++ b/riscv/test/zfh_encoding.cc
@@ -0,0 +1,132 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "riscv/test/zfh_encoding.h" + +#include <cstdint> + +#include "absl/log/log.h" +#include "absl/strings/str_cat.h" +#include "mpact/sim/generic/simple_resource_operand.h" +#include "mpact/sim/generic/type_helpers.h" +#include "riscv/riscv_getter_helpers.h" +#include "riscv/riscv_register.h" +#include "riscv/riscv_state.h" +#include "riscv/test/riscv_getters_zfh.h" +#include "riscv/zfh_bin_decoder.h" +#include "riscv/zfh_decoder.h" +#include "riscv/zfh_enums.h" + +namespace mpact::sim::riscv::zfh { + +using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. + +ZFHEncoding::ZFHEncoding(RiscVState *state) + : state_(state), + inst_word_(0), + opcode_(OpcodeEnum::kNone), + format_(FormatEnum::kNone) { + resource_delay_line_ = + state_->CreateAndAddDelayLine<generic::SimpleResourceDelayLine>(8); + // Initialize getters. + source_op_getters_.emplace(*SourceOpEnum::kNone, []() { return nullptr; }); + dest_op_getters_.emplace(*DestOpEnum::kNone, + [](int latency) { return nullptr; }); + simple_resource_getters_.emplace(*SimpleResourceEnum::kNone, + []() { return nullptr; }); + complex_resource_getters_.emplace( + *ComplexResourceEnum::kNone, + [](int latency, int end) { return nullptr; }); + + AddRiscVZfhSourceScalarGetters<SourceOpEnum, Extractors, RVXRegister>( + source_op_getters_, this); + AddRiscVZfhSourceFloatGetters<SourceOpEnum, Extractors, RVFpRegister>( + source_op_getters_, this); + AddRiscVZfhDestScalarGetters<DestOpEnum, Extractors, RVXRegister>( + dest_op_getters_, this); + AddRiscVZfhDestFloatGetters<DestOpEnum, Extractors, RVFpRegister>( + dest_op_getters_, this); + AddRiscVZfhSimpleResourceGetters<SimpleResourceEnum, Extractors>( + simple_resource_getters_, this); + + // Verify that there are getters for each enum value. + for (int i = *SourceOpEnum::kNone; i < *SourceOpEnum::kPastMaxValue; ++i) { + if (source_op_getters_.find(i) == source_op_getters_.end()) { + LOG(ERROR) << "No getter for source op enum value " << i; + } + } + for (int i = *DestOpEnum::kNone; i < *DestOpEnum::kPastMaxValue; ++i) { + if (dest_op_getters_.find(i) == dest_op_getters_.end()) { + LOG(ERROR) << "No getter for destination op enum value " << i; + } + } + for (int i = *SimpleResourceEnum::kNone; + i < *SimpleResourceEnum::kPastMaxValue; ++i) { + if (simple_resource_getters_.find(i) == simple_resource_getters_.end()) { + LOG(ERROR) << "No getter for simple resource enum value " << i; + } + } +} + +ZFHEncoding::~ZFHEncoding() { delete resource_pool_; } + +void ZFHEncoding::ParseInstruction(uint32_t inst_word) { + inst_word_ = inst_word; + auto [opcode, format] = DecodeRiscVZfhInst32WithFormat(inst_word_); + opcode_ = opcode; + format_ = format; +} + +ResourceOperandInterface *ZFHEncoding::GetComplexResourceOperand( + SlotEnum, int, OpcodeEnum, ComplexResourceEnum resource, int begin, + int end) { + return nullptr; +} + +ResourceOperandInterface *ZFHEncoding::GetSimpleResourceOperand( + SlotEnum, int, OpcodeEnum, SimpleResourceVector &resource_vec, int end) { + return nullptr; +} + +DestinationOperandInterface *ZFHEncoding::GetDestination(SlotEnum, int, + OpcodeEnum opcode, + DestOpEnum dest_op, + int dest_no, + int latency) { + int index = static_cast<int>(dest_op); + auto iter = dest_op_getters_.find(index); + if (iter == dest_op_getters_.end()) { + LOG(ERROR) << absl::StrCat("No getter for destination op enum value ", + index, "for instruction ", + kOpcodeNames[static_cast<int>(opcode)]); + return nullptr; + } + return (iter->second)(latency); +} + +SourceOperandInterface *ZFHEncoding::GetSource(SlotEnum, int, OpcodeEnum opcode, + SourceOpEnum source_op, + int source_no) { + int index = static_cast<int>(source_op); + auto iter = source_op_getters_.find(index); + if (iter == source_op_getters_.end()) { + LOG(ERROR) << absl::StrCat("No getter for source op enum value ", index, + " for instruction ", + kOpcodeNames[static_cast<int>(opcode)]); + return nullptr; + } + return (iter->second)(); +} + +} // namespace mpact::sim::riscv::zfh
diff --git a/riscv/test/zfh_encoding.h b/riscv/test/zfh_encoding.h new file mode 100644 index 0000000..9e77a56 --- /dev/null +++ b/riscv/test/zfh_encoding.h
@@ -0,0 +1,103 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_MPACT_RISCV_TEST_ZFH_ENCODING_H_ +#define THIRD_PARTY_MPACT_RISCV_TEST_ZFH_ENCODING_H_ + +#include <cstdint> +#include <string> + +#include "mpact/sim/generic/simple_resource.h" +#include "mpact/sim/generic/simple_resource_operand.h" +#include "riscv/riscv_encoding_common.h" +#include "riscv/riscv_getter_helpers.h" +#include "riscv/riscv_state.h" +#include "riscv/zfh_bin_decoder.h" +#include "riscv/zfh_decoder.h" +#include "riscv/zfh_enums.h" + +namespace mpact::sim::riscv::zfh { + +// This class provides the interface between the generated instruction decoder +// framework (which is agnostic of the actual bit representation of +// instructions) and the instruction representation. This class provides methods +// to return the opcode, source operands, and destination operands for +// instructions according to the operand fields in the encoding. +class ZFHEncoding : public ZFHEncodingBase, public RiscVEncodingCommon { + public: + explicit ZFHEncoding(RiscVState *state); + ~ZFHEncoding() override; + + void ParseInstruction(uint32_t inst_word); + OpcodeEnum GetOpcode(SlotEnum, int) override { return opcode_; } + FormatEnum GetFormat(SlotEnum, int) { return format_; } + + PredicateOperandInterface *GetPredicate(SlotEnum, int, OpcodeEnum, + PredOpEnum) override { + return nullptr; + } + + ResourceOperandInterface *GetSimpleResourceOperand( + SlotEnum, int, OpcodeEnum, SimpleResourceVector &resource_vec, + int end) override; + + ResourceOperandInterface *GetComplexResourceOperand( + SlotEnum, int, OpcodeEnum, ComplexResourceEnum resource, int begin, + int end) override; + + SourceOperandInterface *GetSource(SlotEnum, int, OpcodeEnum, SourceOpEnum op, + int source_no) override; + + DestinationOperandInterface *GetDestination(SlotEnum, int, OpcodeEnum, + DestOpEnum op, int dest_no, + int latency) override; + + int GetLatency(SlotEnum, int, OpcodeEnum, DestOpEnum, int) override { + return 0; + } + + // Methods inherited from RiscVEncodingCommon. + RiscVState *state() const override { return state_; } + generic::SimpleResourcePool *resource_pool() override { + return resource_pool_; + } + uint32_t inst_word() const override { return inst_word_; } + + const SourceOpGetterMap &source_op_getters() { return source_op_getters_; } + const DestOpGetterMap &dest_op_getters() { return dest_op_getters_; } + const SimpleResourceGetterMap &simple_resource_getters() { + return simple_resource_getters_; + } + const ComplexResourceGetterMap &complex_resource_getters() { + return complex_resource_getters_; + } + + private: + std::string GetSimpleResourceName(SimpleResourceEnum resource_enum); + + RiscVState *state_; + uint32_t inst_word_; + OpcodeEnum opcode_; + FormatEnum format_; + SourceOpGetterMap source_op_getters_; + DestOpGetterMap dest_op_getters_; + SimpleResourceGetterMap simple_resource_getters_; + ComplexResourceGetterMap complex_resource_getters_; + generic::SimpleResourceDelayLine *resource_delay_line_ = nullptr; + generic::SimpleResourcePool *resource_pool_ = nullptr; +}; + +} // namespace mpact::sim::riscv::zfh + +#endif // THIRD_PARTY_MPACT_RISCV_TEST_ZFH_ENCODING_H_
diff --git a/riscv/test/zfh_encoding_test.cc b/riscv/test/zfh_encoding_test.cc new file mode 100644 index 0000000..dd37b8b --- /dev/null +++ b/riscv/test/zfh_encoding_test.cc
@@ -0,0 +1,412 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "riscv/test/zfh_encoding.h" + +#include <sys/types.h> + +#include <cstdint> +#include <ios> +#include <memory> +#include <tuple> + +#include "absl/random/random.h" +#include "absl/strings/str_cat.h" +#include "mpact/sim/generic/immediate_operand.h" +#include "mpact/sim/generic/operand_interface.h" +#include "mpact/sim/generic/register.h" +#include "mpact/sim/generic/type_helpers.h" +#include "mpact/sim/util/memory/flat_demand_memory.h" +#include "riscv/riscv_register.h" +#include "riscv/riscv_state.h" +#include "riscv/zfh_enums.h" +#include "testing/base/public/gunit.h" + +// Test that hand crafted zfh instructions are decoded and parsed correctly. + +namespace { + +using ::mpact::sim::generic::operator*; // NOLINT: clang-tidy false positive. +using ::mpact::sim::generic::ImmediateOperand; +using ::mpact::sim::generic::RegisterDestinationOperand; +using ::mpact::sim::generic::SourceOperandInterface; +using mpact::sim::riscv::RiscVState; +using mpact::sim::riscv::RiscVXlen; +using mpact::sim::riscv::RVFpRegister; +using mpact::sim::riscv::RVXRegister; +using mpact::sim::util::FlatDemandMemory; + +using mpact::sim::riscv::zfh::kComplexResourceNames; +using mpact::sim::riscv::zfh::kDestOpNames; +using mpact::sim::riscv::zfh::kSourceOpNames; + +using SlotEnum = mpact::sim::riscv::zfh::SlotEnum; +using OpcodeEnum = mpact::sim::riscv::zfh::OpcodeEnum; +using SourceOpEnum = mpact::sim::riscv::zfh::SourceOpEnum; +using DestOpEnum = mpact::sim::riscv::zfh::DestOpEnum; +using ComplexResourceEnum = mpact::sim::riscv::zfh::ComplexResourceEnum; + +using mpact::sim::riscv::zfh::ZFHEncoding; + +// imm12 | rs1 | | rd | opcode +constexpr uint32_t kFlh = 0b000000000000'00000'001'00000'0000111; +// imm7 | rs2 | rs1 | |imm5 | opcode +constexpr uint32_t kFsh = 0b0000000'00000'00000'001'00000'0100111; +// func7 | | rs1 |fn3| rd | opcode +constexpr uint32_t kFmvXh = 0b1110010'00000'00000'000'00000'1010011; +// func7 | | rs1 |fn3| rd | opcode +constexpr uint32_t kFmvHx = 0b1111010'00000'00000'000'00000'1010011; +// func7 | | rs1 |rm | rd | opcode +constexpr uint32_t kFcvtSh = 0b0100000'00010'00000'000'00000'1010011; +// func7 | | rs1 |rm | rd | opcode +constexpr uint32_t kFcvtHs = 0b0100010'00000'00000'000'00000'1010011; +// func7 | | rs1 |rm | rd | opcode +constexpr uint32_t kFcvtDh = 0b0100001'00010'00000'000'00000'1010011; +// func7 | | rs1 |rm | rd | opcode +constexpr uint32_t kFcvtHd = 0b0100010'00001'00000'000'00000'1010011; + +class ZfhEncodingTest : public testing::Test { + protected: + ZfhEncodingTest() { + state_ = new RiscVState("test", RiscVXlen::RV32, &memory_); + enc_ = new ZFHEncoding(state_); + } + + ~ZfhEncodingTest() override { + delete enc_; + delete state_; + } + + void ScalarRs1Helper(uint32_t, OpcodeEnum); + void FloatFrdHelper(uint32_t, OpcodeEnum); + void FloatSourceHelper(uint32_t, OpcodeEnum, int); + void FloatSourceHelper(uint32_t, OpcodeEnum, SourceOpEnum, int); + void FloatFrs1Helper(uint32_t, OpcodeEnum); + void FloatFrs2Helper(uint32_t, OpcodeEnum); + void FloatRmHelper(uint32_t, OpcodeEnum); + + FlatDemandMemory memory_; + RiscVState *state_; + ZFHEncoding *enc_; + absl::BitGen gen_; +}; + +void ZfhEncodingTest::ScalarRs1Helper(uint32_t binary_instruction, + OpcodeEnum opcode_enum) { + int rs1_offset = 15; + uint32_t base_instruction = binary_instruction & ~(0x0000'001F << rs1_offset); + for (int rs1_index = 0; rs1_index < 32; ++rs1_index) { + uint32_t expected_value = rs1_index ? absl::Uniform<uint32_t>(gen_) : 0; + + // Set the register value with a test value. + RVXRegister *rs1_reg; + std::tie(rs1_reg, std::ignore) = state_->GetRegister<RVXRegister>( + absl::StrCat("x", static_cast<uint32_t>(rs1_index))); + rs1_reg->data_buffer()->Set<uint32_t>(0, expected_value); + + // Parse the instruction and get the source operand. + 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)); + + // Pull the value from the source operand and compare it to the expected + // value. + EXPECT_EQ(src->AsUint32(0), expected_value) + << "rs1_index: " << rs1_index << ", expected_value: " << std::hex + << expected_value << ", observed value: " << std::hex + << src->AsUint32(0); + } +} + +void ZfhEncodingTest::FloatFrdHelper(uint32_t binary_instruction, + OpcodeEnum opcode_enum) { + int frd_offset = 7; + uint32_t base_instruction = binary_instruction & ~(0x0000'001F << frd_offset); + for (int frd_index = 0; frd_index < 32; ++frd_index) { + uint64_t expected_value = absl::Uniform<uint64_t>(gen_); + uint32_t frd_adjustment = static_cast<uint32_t>(frd_index) << frd_offset; + + // Set the register value with a test value. + RVFpRegister *frd_reg; + std::tie(frd_reg, std::ignore) = + state_->GetRegister<RVFpRegister>(absl::StrCat("f", frd_index)); + frd_reg->data_buffer()->Set<uint64_t>(0, expected_value); + + // Parse the instruction and get the destination operand. + enc_->ParseInstruction(base_instruction | frd_adjustment); + std::unique_ptr<RegisterDestinationOperand<RVFpRegister>> dst( + static_cast<RegisterDestinationOperand<RVFpRegister> *>( + enc_->GetDestination(SlotEnum::kRiscv32ZfhMin, 0, opcode_enum, + DestOpEnum::kFrd, 0, 0))); + + // Pull the value from the destination operand and compare it to the + // expected value. + uint64_t observed_value = + dst->GetRegister()->data_buffer()->Get<uint64_t>(0); + EXPECT_EQ(observed_value, expected_value) + << "frd_index: " << frd_index << ", expected_value: " << std::hex + << expected_value << ", observed value: " << std::hex << observed_value; + } +} + +void ZfhEncodingTest::FloatSourceHelper(uint32_t binary_instruction, + OpcodeEnum opcode_enum, + SourceOpEnum source_op_enum, + int offset) { + uint32_t base_instruction = binary_instruction & ~(0x0000'001F << offset); + for (uint32_t frs1_index = 0; frs1_index < 32; ++frs1_index) { + uint32_t src_adjustment = frs1_index << offset; + uint64_t expected_value = absl::Uniform<uint64_t>(gen_); + + // Set the register value with a test value. + RVFpRegister *frs1_reg; + std::tie(frs1_reg, std::ignore) = + state_->GetRegister<RVFpRegister>(absl::StrCat("f", frs1_index)); + frs1_reg->data_buffer()->Set<uint64_t>(0, expected_value); + + // 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)); + + // Pull the value from the source operand and compare it to the expected + // value. + EXPECT_EQ(src->AsUint64(0), expected_value) + << "frs1_index: " << frs1_index << ", expected_value: " << std::hex + << expected_value << ", observed value: " << std::hex + << src->AsUint64(0); + } +} + +void ZfhEncodingTest::FloatFrs1Helper(uint32_t binary_instruction, + OpcodeEnum opcode_enum) { + FloatSourceHelper(binary_instruction, opcode_enum, SourceOpEnum::kFrs1, 15); +} + +void ZfhEncodingTest::FloatFrs2Helper(uint32_t binary_instruction, + OpcodeEnum opcode_enum) { + FloatSourceHelper(binary_instruction, opcode_enum, SourceOpEnum::kFrs2, 20); +} + +void ZfhEncodingTest::FloatRmHelper(uint32_t binary_instruction, + OpcodeEnum opcode_enum) { + 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)); + EXPECT_EQ(src->AsUint32(0), rm); + } +} + +TEST_F(ZfhEncodingTest, SourceOperands) { + auto &getters = enc_->source_op_getters(); + for (int i = *SourceOpEnum::kNone; i < *SourceOpEnum::kPastMaxValue; ++i) { + EXPECT_TRUE(getters.contains(i)) << "No source operand for enum value " << i + << " (" << kSourceOpNames[i] << ")"; + } +} + +TEST_F(ZfhEncodingTest, DestOperands) { + auto &getters = enc_->dest_op_getters(); + for (int i = *DestOpEnum::kNone; i < *DestOpEnum::kPastMaxValue; ++i) { + EXPECT_TRUE(getters.contains(i)) << "No dest operand for enum value " << i + << " (" << kDestOpNames[i] << ")"; + } +} + +// TODO(julianmb): Add simple resource getters when appropriate. +// TEST_F(ZfhEncodingTest, SimpleResources) { +// auto &getters = enc_->simple_resource_getters(); +// for (int i = *SimpleResourceEnum::kNone; +// i < *SimpleResourceEnum::kPastMaxValue; ++i) { +// EXPECT_TRUE(getters.contains(i)) << "No source operand for enum value " +// << i +// << " (" << kSimpleResourceNames[i] << +// ")"; +// } +// } + +TEST_F(ZfhEncodingTest, ComplexResources) { + auto &getters = enc_->source_op_getters(); + for (int i = *ComplexResourceEnum::kNone; + i < *ComplexResourceEnum::kPastMaxValue; ++i) { + EXPECT_TRUE(getters.contains(i)) << "No source operand for enum value " << i + << " (" << kComplexResourceNames[i] << ")"; + } +} + +TEST_F(ZfhEncodingTest, Flh) { + enc_->ParseInstruction(kFlh); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFlh); +} + +TEST_F(ZfhEncodingTest, Flh_imm12) { + for (int iter = 0; iter < 1000; ++iter) { + int32_t expected_imm = + absl::Uniform<int32_t>(gen_, -1 * (1 << 11), 1 << 11); + bool sign = expected_imm < 0; + uint32_t imm_adjustment = + (sign ? 0x8000'0000 : 0) | ((expected_imm & 0x0000'07FF) << 20); + enc_->ParseInstruction(kFlh | imm_adjustment); + std::unique_ptr<ImmediateOperand<int32_t>> src( + static_cast<ImmediateOperand<int32_t> *>( + enc_->GetSource(SlotEnum::kRiscv32ZfhMin, 0, OpcodeEnum::kFlh, + SourceOpEnum::kIImm12, 0))); + EXPECT_EQ(src->AsInt32(0), expected_imm); + } +} + +TEST_F(ZfhEncodingTest, Flh_rs1) { ScalarRs1Helper(kFlh, OpcodeEnum::kFlh); } + +TEST_F(ZfhEncodingTest, Flh_frd) { FloatFrdHelper(kFlh, OpcodeEnum::kFlh); } + +TEST_F(ZfhEncodingTest, Fsh) { + enc_->ParseInstruction(kFsh); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFsh); +} + +TEST_F(ZfhEncodingTest, Fsh_imm12) { + for (int iter = 0; iter < 1000; ++iter) { + int32_t expected_imm = + absl::Uniform<int32_t>(gen_, -1 * (1 << 11), 1 << 11); + bool sign = expected_imm < 0; + uint32_t imm_adjustment = (sign ? 0x8000'0000 : 0) | + ((expected_imm & 0x0000'001F) << 7) | + ((expected_imm & 0x0000'07E0) << 20); + enc_->ParseInstruction(kFsh | imm_adjustment); + std::unique_ptr<ImmediateOperand<int32_t>> src( + static_cast<ImmediateOperand<int32_t> *>( + enc_->GetSource(SlotEnum::kRiscv32ZfhMin, 0, OpcodeEnum::kFsh, + SourceOpEnum::kSImm12, 0))); + EXPECT_EQ(src->AsInt32(0), expected_imm); + } +} + +TEST_F(ZfhEncodingTest, Fsh_rs1) { ScalarRs1Helper(kFsh, OpcodeEnum::kFsh); } + +TEST_F(ZfhEncodingTest, Fsh_frs2) { FloatFrs2Helper(kFsh, OpcodeEnum::kFsh); } + +TEST_F(ZfhEncodingTest, FmvXh) { + enc_->ParseInstruction(kFmvXh); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFmvXh); +} + +TEST_F(ZfhEncodingTest, FmvXh_frs1) { + FloatFrs1Helper(kFmvXh, OpcodeEnum::kFmvXh); +} + +TEST_F(ZfhEncodingTest, FmvXh_rd) { + for (uint32_t rd_index = 1; rd_index < 32; ++rd_index) { + uint32_t rd_adjustment = rd_index << 7; + uint32_t expected_value = absl::Uniform<uint32_t>(gen_); + RVXRegister *rd_reg; + std::tie(rd_reg, std::ignore) = + state_->GetRegister<RVXRegister>(absl::StrCat("x", rd_index)); + rd_reg->data_buffer()->Set<uint32_t>(0, expected_value); + 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))); + uint32_t observed_value = + dst->GetRegister()->data_buffer()->Get<uint32_t>(0); + EXPECT_EQ(observed_value, expected_value); + } +} + +TEST_F(ZfhEncodingTest, FmvHx) { + enc_->ParseInstruction(kFmvHx); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFmvHx); +} + +TEST_F(ZfhEncodingTest, FmvHx_rs1) { + ScalarRs1Helper(kFmvHx, OpcodeEnum::kFmvHx); +} + +TEST_F(ZfhEncodingTest, FmvHx_frd) { + FloatFrdHelper(kFmvHx, OpcodeEnum::kFmvHx); +} + +TEST_F(ZfhEncodingTest, FcvtSh) { + enc_->ParseInstruction(kFcvtSh); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFcvtSh); +} + +TEST_F(ZfhEncodingTest, FcvtSh_frs1) { + FloatFrs1Helper(kFcvtSh, OpcodeEnum::kFcvtSh); +} + +TEST_F(ZfhEncodingTest, FcvtSh_frd) { + FloatFrdHelper(kFcvtSh, OpcodeEnum::kFcvtSh); +} + +TEST_F(ZfhEncodingTest, FcvtSh_rm) { + FloatRmHelper(kFcvtSh, OpcodeEnum::kFcvtSh); +} + +TEST_F(ZfhEncodingTest, FcvtHs) { + enc_->ParseInstruction(kFcvtHs); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFcvtHs); +} + +TEST_F(ZfhEncodingTest, FcvtHs_frs1) { + FloatFrs1Helper(kFcvtHs, OpcodeEnum::kFcvtHs); +} + +TEST_F(ZfhEncodingTest, FcvtHs_frd) { + FloatFrdHelper(kFcvtHs, OpcodeEnum::kFcvtHs); +} + +TEST_F(ZfhEncodingTest, FcvtHs_rm) { + FloatRmHelper(kFcvtHs, OpcodeEnum::kFcvtHs); +} + +TEST_F(ZfhEncodingTest, FcvtDh) { + enc_->ParseInstruction(kFcvtDh); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFcvtDh); +} + +TEST_F(ZfhEncodingTest, FcvtDh_frs1) { + FloatFrs1Helper(kFcvtDh, OpcodeEnum::kFcvtDh); +} + +TEST_F(ZfhEncodingTest, FcvtDh_frd) { + FloatFrdHelper(kFcvtDh, OpcodeEnum::kFcvtDh); +} + +TEST_F(ZfhEncodingTest, FcvtDh_rm) { + FloatRmHelper(kFcvtDh, OpcodeEnum::kFcvtDh); +} + +TEST_F(ZfhEncodingTest, FcvtHd) { + enc_->ParseInstruction(kFcvtHd); + EXPECT_EQ(enc_->GetOpcode(SlotEnum::kRiscv32ZfhMin, 0), OpcodeEnum::kFcvtHd); +} + +TEST_F(ZfhEncodingTest, FcvtHd_frs1) { + FloatFrs1Helper(kFcvtHd, OpcodeEnum::kFcvtHd); +} + +TEST_F(ZfhEncodingTest, FcvtHd_frd) { + FloatFrdHelper(kFcvtHd, OpcodeEnum::kFcvtHd); +} + +TEST_F(ZfhEncodingTest, FcvtHd_rm) { + FloatRmHelper(kFcvtHd, OpcodeEnum::kFcvtHd); +} + +} // namespace