Adds RiscV Zicond instruction semantic functions and isa/bin_fmt defs. Adds capability to combine instruction groups other than in decoders in .bin_fmt files. PiperOrigin-RevId: 689844873 Change-Id: I9ff8aabcec8121214074731a962f6a8a1a174d87
diff --git a/riscv/BUILD b/riscv/BUILD index b3b03f7..bea5ac3 100644 --- a/riscv/BUILD +++ b/riscv/BUILD
@@ -142,6 +142,7 @@ "riscv_m_instructions.cc", "riscv_priv_instructions.cc", "riscv_zfencei_instructions.cc", + "riscv_zicond_instructions.cc", "riscv_zicsr_instructions.cc", ], hdrs = [ @@ -153,6 +154,7 @@ "riscv_m_instructions.h", "riscv_priv_instructions.h", "riscv_zfencei_instructions.h", + "riscv_zicond_instructions.h", "riscv_zicsr_instructions.h", ], copts = [ @@ -250,6 +252,27 @@ ], ) +cc_library( + name = "riscv_rvm23_instructions", + srcs = [ + "riscv_zicond_instructions.cc", + ], + hdrs = [ + "riscv_zicond_instructions.h", + ], + copts = [ + "-O3", + "-ffp-model=strict", + ], + deps = [ + ":riscv_g", + ":riscv_state", + ":riscv_v", + "@com_google_mpact-sim//mpact/sim/generic:core", + "@com_google_mpact-sim//mpact/sim/generic:instruction", + ], +) + mpact_isa_decoder( name = "riscv32g_isa", src = "riscv32g.isa", @@ -335,6 +358,43 @@ ) mpact_isa_decoder( + name = "rvm23_isa", + src = "rvm23.isa", + includes = [ + "riscv32g.isa", + "riscv32v.isa", + "riscv32zb.isa", + "riscv_vector.isa", + "riscv_zicond.isa", + ], + isa_name = "RVM23", + prefix = "rvm23", + deps = [ + ":riscv_bitmanip_instructions", + ":riscv_g", + ":riscv_rvm23_instructions", + ":riscv_v", + "@com_google_absl//absl/functional:bind_front", + ], +) + +mpact_bin_fmt_decoder( + name = "rvm23_bin_fmt", + src = "rvm23.bin_fmt", + decoder_name = "RVM23", + includes = [ + "riscv32g.bin_fmt", + "riscv32v.bin_fmt", + "riscv32zb.bin_fmt", + "riscv_zicond.bin_fmt", + ], + prefix = "rvm23", + deps = [ + ":rvm23_isa", + ], +) + +mpact_isa_decoder( name = "riscv64g_isa", src = "riscv64g.isa", includes = [],
diff --git a/riscv/riscv32zb.isa b/riscv/riscv32zb.isa index d878c3f..f8539f9 100644 --- a/riscv/riscv32zb.isa +++ b/riscv/riscv32zb.isa
@@ -21,11 +21,18 @@ disasm widths = {-18}; +// Slot that gathers all the bitmanipulation instructions. +slot riscv32_zb : + riscv32_zba, + riscv32_zbb, + riscv32_zbb_imm, + riscv32_zbc, + riscv32_zbs, + riscv32_zbs_imm { +} + slot riscv32_zba { default size = 4; - default opcode = - disasm: "Illegal instruction at 0x%(@:08x)", - semfunc: "&RiscVIllegalInstruction"; resources ThreeOp = { next_pc, rs1, rs2 : rd[..rd]}; opcodes { sh1add{: rs1, rs2, const1 : rd}, @@ -45,9 +52,6 @@ slot riscv32_zbb { default size = 4; - default opcode = - disasm: "Illegal instruction at 0x%(@:08x)", - semfunc: "&RiscVIllegalInstruction"; resources TwoOp = { next_pc, rs1 : rd[..rd]}; resources ThreeOp = { next_pc, rs1, rs2 : rd[..rd]}; opcodes { @@ -132,9 +136,6 @@ slot riscv32_zbb_imm { default size = 4; - default opcode = - disasm: "Illegal instruction at 0x%(@:08x)", - semfunc: "&RiscVIllegalInstruction"; resources TwoOp = { next_pc, rs1 : rd[..rd]}; resources ThreeOp = { next_pc, rs1, rs2 : rd[..rd]}; opcodes { @@ -147,9 +148,6 @@ slot riscv32_zbc { default size = 4; - default opcode = - disasm: "Illegal instruction at 0x%(@:08x)", - semfunc: "&RiscVIllegalInstruction"; resources ThreeOp = { next_pc, rs1, rs2 : rd[..rd]}; opcodes { // Carry-less multiplication. @@ -170,9 +168,6 @@ slot riscv32_zbs { default size = 4; - default opcode = - disasm: "Illegal instruction at 0x%(@:08x)", - semfunc: "&RiscVIllegalInstruction"; resources TwoOp = { next_pc, rs1 : rd[..rd]}; resources ThreeOp = { next_pc, rs1, rs2 : rd[..rd]}; opcodes { @@ -198,9 +193,6 @@ slot riscv32_zbs_imm { default size = 4; - default opcode = - disasm: "Illegal instruction at 0x%(@:08x)", - semfunc: "&RiscVIllegalInstruction"; resources TwoOp = { next_pc, rs1 : rd[..rd]}; opcodes { // Single-bit instructions.
diff --git a/riscv/riscv64zb.isa b/riscv/riscv64zb.isa index edfa18f..f5562dc 100644 --- a/riscv/riscv64zb.isa +++ b/riscv/riscv64zb.isa
@@ -23,6 +23,15 @@ disasm widths = {-18}; +// Slot that gathers all the bitmanipulation instructions. +slot riscv64_zb : + riscv64_zba, + riscv64_zbb, + riscv64_zbb_imm, + riscv64_zbc, + riscv64_zbs_imm { +} + slot riscv64_zba : riscv32_zba { default size = 4; default opcode =
diff --git a/riscv/riscv_zicond.bin_fmt b/riscv/riscv_zicond.bin_fmt new file mode 100644 index 0000000..5a37c8e --- /dev/null +++ b/riscv/riscv_zicond.bin_fmt
@@ -0,0 +1,20 @@ +// Copyright 2024 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. + +// This file contains the encoding for the Zicond instructions. + +instruction group RiscVZicond[32] : Inst32Format { + czero_eqz: RType : func7 == 0b000'0111, func3 == 0b101, opcode == 0b011'0011; + czero_nez: RType : func7 == 0b000'0111, func3 == 0b111, opcode == 0b011'0011; +} \ No newline at end of file
diff --git a/riscv/riscv_zicond.isa b/riscv/riscv_zicond.isa new file mode 100644 index 0000000..03976bd --- /dev/null +++ b/riscv/riscv_zicond.isa
@@ -0,0 +1,47 @@ +// Copyright 2024 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. + +// This file contains the ISA description for the RiscV32/64 conditional zeroing +// extension. + +includes { + #include "riscv/riscv_zicond_instructions.h" +} + +disasm widths = {-18}; + +slot riscv32_zicond { + default size = 4; + default opcode = + disasm: "Illegal instruction at 0x%(@:08x)", + semfunc: "&RiscVIllegalInstruction"; + resources ThreeOp = { next_pc, rs1, rs2 : rd[..rd]}; + opcodes { + czero_eqz{: rs1, rs2: rd}, + resources: ThreeOp, + disasm: "czero.eqz", "%rd, %rs1, %rs2", + semfunc: "&RV32::RiscVCzeroEqz"; + czero_nez{: rs1, rs2: rd}, + resources: ThreeOp, + disasm: "czero.nez", "%rd, %rs1, %rs2", + semfunc: "&RV32::RiscVCzeroNez"; + } +} + +slot riscv64_zicond : riscv32_zicond{ + opcodes { + czero_eqz = override, semfunc: "&RV64::RiscVCzeroEqz"; + czero_nez = override, semfunc: "&RV64::RiscVCzeroNez"; + } +} \ No newline at end of file
diff --git a/riscv/riscv_zicond_instructions.cc b/riscv/riscv_zicond_instructions.cc new file mode 100644 index 0000000..a81bccd --- /dev/null +++ b/riscv/riscv_zicond_instructions.cc
@@ -0,0 +1,65 @@ +// Copyright 2024 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/riscv_zicond_instructions.h" + +#include "mpact/sim/generic/instruction.h" +#include "riscv/riscv_instruction_helpers.h" +#include "riscv/riscv_register.h" + +namespace mpact::sim::riscv { + +namespace RV32 { + +using RegType = RV32Register; +using RegValue = typename RegType::ValueType; + +// If rs2 is zero, set the destination register to 0, otherwise copy rs1 to +// the destination register. +void RiscVCzeroEqz(const generic::Instruction *inst) { + RiscVBinaryOp<RegType, RegValue, RegValue>( + inst, [](RegValue rs1, RegValue rs2) { return rs2 == 0 ? 0 : rs1; }); +} + +// If rs2 is non-zero, set the destination register to 0, otherwise copy rs1 to +// the destination register. +void RiscVCzeroNez(const generic::Instruction *inst) { + RiscVBinaryOp<RegType, RegValue, RegValue>( + inst, [](RegValue rs1, RegValue rs2) { return rs2 != 0 ? 0 : rs1; }); +} + +} // namespace RV32 + +namespace RV64 { + +using RegType = RV64Register; +using RegValue = typename RegType::ValueType; + +// If rs2 is zero, set the destination register to 0, otherwise copy rs1 to +// the destination register. +void RiscVCzeroEqz(const generic::Instruction *inst) { + RiscVBinaryOp<RegType, RegValue, RegValue>( + inst, [](RegValue rs1, RegValue rs2) { return rs2 == 0 ? 0 : rs1; }); +} + +// If rs2 is non-zero, set the destination register to 0, otherwise copy rs1 to +// the destination register. +void RiscVCzeroNez(const generic::Instruction *inst) { + RiscVBinaryOp<RegType, RegValue, RegValue>( + inst, [](RegValue rs1, RegValue rs2) { return rs2 != 0 ? 0 : rs1; }); +} + +} // namespace RV64 + +} // namespace mpact::sim::riscv
diff --git a/riscv/riscv_zicond_instructions.h b/riscv/riscv_zicond_instructions.h new file mode 100644 index 0000000..f70b85b --- /dev/null +++ b/riscv/riscv_zicond_instructions.h
@@ -0,0 +1,45 @@ +// Copyright 2024 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_RISCV_ZICOND_INSTRUCTIONS_H_ +#define THIRD_PARTY_MPACT_RISCV_RISCV_ZICOND_INSTRUCTIONS_H_ + +#include "mpact/sim/generic/instruction.h" + +// This file declares the semantic functions for the Zicond instructions. + +namespace mpact::sim::riscv { + +using ::mpact::sim::generic::Instruction; + +// Each of the instruction semantic functions below takes 2 source operands and +// a single destination operand. + +namespace RV32 { + +void RiscVCzeroEqz(const Instruction *inst); +void RiscVCzeroNez(const Instruction *inst); + +} // namespace RV32 + +namespace RV64 { + +void RiscVCzeroEqz(const Instruction *inst); +void RiscVCzeroNez(const Instruction *inst); + +} // namespace RV64 + +} // namespace mpact::sim::riscv + +#endif // THIRD_PARTY_MPACT_RISCV_RISCV_ZICOND_INSTRUCTIONS_H_
diff --git a/riscv/rvm23.bin_fmt b/riscv/rvm23.bin_fmt new file mode 100644 index 0000000..ffe0e53 --- /dev/null +++ b/riscv/rvm23.bin_fmt
@@ -0,0 +1,40 @@ +// Copyright 2024 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. + +decoder RVM23 { + namespace mpact::sim::riscv::rvm23; + opcode_enum = "OpcodeEnum"; + includes { + #include "riscv/rvm23_decoder.h" + } + + RVM23Inst32 = { + // GP instructions. + RiscVGInst32, + // Bitmanipulation instructions. + RiscVZbaInst32, + RiscVZbbInst32, + RiscVZbcInst32, + RiscVZbsInst32, + // Conditional zero. + RiscVZicond, + }; + RVM23Inst16 = { + RiscVCInst16, + }; +} + +#include "riscv/riscv32g.bin_fmt" +#include "riscv/riscv32zb.bin_fmt" +#include "riscv/riscv_zicond.bin_fmt" \ No newline at end of file
diff --git a/riscv/rvm23.isa b/riscv/rvm23.isa new file mode 100644 index 0000000..bd2d869 --- /dev/null +++ b/riscv/rvm23.isa
@@ -0,0 +1,43 @@ +// Copyright 2024 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. + +isa RVM23 { + namespace mpact::sim::riscv::rvm23; + slots { + rvm23; + } +} + +#include "riscv/riscv32g.isa" +#include "riscv/riscv32zb.isa" +#include "riscv/riscv_vector.isa" +#include "riscv/riscv_zicond.isa" + +slot rvm23 : + riscv32i, + riscv32c, + riscv32m, + riscv32_amo_arithmetic, + riscv32f, + riscv32d, + zicsr, + zfencei, + privileged, + riscv_vector, + riscv32_zb, + riscv32_zicond { + default opcode = + disasm: "Illegal instruction at 0x%(@:08x)", + semfunc: "&RiscVIllegalInstruction"; +}
diff --git a/riscv/test/BUILD b/riscv/test/BUILD index 5ada549..927fafb 100644 --- a/riscv/test/BUILD +++ b/riscv/test/BUILD
@@ -743,3 +743,20 @@ "@com_google_mpact-sim//mpact/sim/generic:counters", ], ) + +cc_test( + name = "riscv_zicond_instructions_test", + size = "small", + srcs = ["riscv_zicond_instructions_test.cc"], + deps = [ + "//riscv:riscv_g", + "//riscv:riscv_state", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/log:check", + "@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:instruction", + ], +)
diff --git a/riscv/test/riscv_zicond_instructions_test.cc b/riscv/test/riscv_zicond_instructions_test.cc new file mode 100644 index 0000000..576a331 --- /dev/null +++ b/riscv/test/riscv_zicond_instructions_test.cc
@@ -0,0 +1,174 @@ +#include "riscv/riscv_zicond_instructions.h" + +#include <cstdint> +#include <string> +#include <tuple> +#include <vector> + +#include "absl/container/flat_hash_map.h" +#include "absl/log/check.h" +#include "absl/strings/string_view.h" +#include "googlemock/include/gmock/gmock.h" +#include "mpact/sim/generic/arch_state.h" +#include "mpact/sim/generic/data_buffer.h" +#include "mpact/sim/generic/instruction.h" +#include "riscv/riscv_register.h" + +namespace { + +using ::mpact::sim::generic::ArchState; +using ::mpact::sim::generic::Instruction; +using ::mpact::sim::riscv::RV32Register; +using ::mpact::sim::riscv::RV64Register; + +constexpr uint32_t kInstAddress = 0x2468; +constexpr char kX1[] = "x1"; +constexpr char kX2[] = "x2"; +constexpr char kX3[] = "x3"; +constexpr uint32_t kVal1 = 0x12345678; +constexpr uint32_t kVal2 = 0x87654321; +constexpr uint32_t kVal3 = 0xdeadbeef; + +class TestState : public ArchState { + public: + TestState() : ArchState("test") {} +}; + +class RiscVZicondInstructionTest : public testing::Test { + public: + RiscVZicondInstructionTest() { + instruction_ = new Instruction(kInstAddress, &state_); + instruction_->set_size(4); + for (auto reg_name : {kX1, kX2, kX3}) { + rv32_regs_.insert({reg_name, new RV32Register(&state_, reg_name)}); + rv64_regs_.insert({reg_name, new RV64Register(&state_, reg_name)}); + } + } + + ~RiscVZicondInstructionTest() override { + delete instruction_; + for (auto reg : rv32_regs_) delete reg.second; + for (auto reg : rv64_regs_) delete reg.second; + } + + // Initializes the semantic function of the instruction object. + void SetSemanticFunction(Instruction::SemanticFunction fcn) { + instruction_->set_semantic_function(fcn); + } + + // Returns the value of the named register. + template <typename RegisterType> + typename RegisterType::ValueType GetRegisterValue( + absl::string_view reg_name) { + RegisterType *reg; + if constexpr (std::is_same_v<RegisterType, RV32Register>) { + reg = rv32_regs_[reg_name]; + } else { + reg = rv64_regs_[reg_name]; + } + CHECK_NE(reg, nullptr); + return reg->data_buffer()->template Get<typename RegisterType::ValueType>( + 0); + } + + template <typename RegisterType> + void AppendRegisterOperands(const std::vector<std::string> &sources, + const std::vector<std::string> &destinations) { + absl::flat_hash_map<std::string, RegisterType *> *regs; + if constexpr (std::is_same_v<RegisterType, RV32Register>) { + regs = &rv32_regs_; + } else { + regs = &rv64_regs_; + } + for (auto src : sources) { + auto *reg = (*regs)[src]; + CHECK_NE(reg, nullptr); + instruction_->AppendSource(reg->CreateSourceOperand()); + } + for (auto dest : destinations) { + auto *reg = (*regs)[dest]; + CHECK_NE(reg, nullptr); + instruction_->AppendDestination(reg->CreateDestinationOperand(0)); + } + } + + template <typename RegisterType> + void SetRegisterValues( + const std::vector< + std::tuple<std::string, typename RegisterType::ValueType>> + values) { + absl::flat_hash_map<std::string, RegisterType *> *regs; + if constexpr (std::is_same_v<RegisterType, RV32Register>) { + regs = &rv32_regs_; + } else { + regs = &rv64_regs_; + } + for (auto &[reg_name, value] : values) { + auto *reg = (*regs)[reg_name]; + CHECK_NE(reg, nullptr); + auto *db = + state_.db_factory()->Allocate<typename RegisterType::ValueType>(1); + db->template Set<typename RegisterType::ValueType>(0, value); + reg->SetDataBuffer(db); + db->DecRef(); + } + } + + Instruction *instruction() { return instruction_; } + + private: + TestState state_; + Instruction *instruction_; + absl::flat_hash_map<std::string, RV32Register *> rv32_regs_; + absl::flat_hash_map<std::string, RV64Register *> rv64_regs_; +}; + +TEST_F(RiscVZicondInstructionTest, RV32CzeroEqz) { + using Reg = RV32Register; + AppendRegisterOperands<Reg>({kX1, kX2}, {kX3}); + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVCzeroEqz); + SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, kVal2}, {kX3, kVal3}}); + instruction()->Execute(nullptr); + EXPECT_EQ(GetRegisterValue<Reg>(kX3), kVal1); + SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, 0}, {kX3, kVal3}}); + instruction()->Execute(nullptr); + EXPECT_EQ(GetRegisterValue<Reg>(kX3), 0); +} + +TEST_F(RiscVZicondInstructionTest, RV32CzeroNez) { + using Reg = RV32Register; + AppendRegisterOperands<Reg>({kX1, kX2}, {kX3}); + SetSemanticFunction(&::mpact::sim::riscv::RV32::RiscVCzeroNez); + SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, 0}, {kX3, kVal3}}); + instruction()->Execute(nullptr); + EXPECT_EQ(GetRegisterValue<Reg>(kX3), kVal1); + SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, kVal2}, {kX3, kVal3}}); + instruction()->Execute(nullptr); + EXPECT_EQ(GetRegisterValue<Reg>(kX3), 0); +} + +TEST_F(RiscVZicondInstructionTest, RV64CzeroEqz) { + using Reg = RV64Register; + AppendRegisterOperands<Reg>({kX1, kX2}, {kX3}); + SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVCzeroEqz); + SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, kVal2}, {kX3, kVal3}}); + instruction()->Execute(nullptr); + EXPECT_EQ(GetRegisterValue<Reg>(kX3), kVal1); + SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, 0}, {kX3, kVal3}}); + instruction()->Execute(nullptr); + EXPECT_EQ(GetRegisterValue<Reg>(kX3), 0); +} + +TEST_F(RiscVZicondInstructionTest, RV64CzeroNez) { + using Reg = RV64Register; + AppendRegisterOperands<Reg>({kX1, kX2}, {kX3}); + SetSemanticFunction(&::mpact::sim::riscv::RV64::RiscVCzeroNez); + SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, 0}, {kX3, kVal3}}); + instruction()->Execute(nullptr); + EXPECT_EQ(GetRegisterValue<Reg>(kX3), kVal1); + SetRegisterValues<Reg>({{kX1, kVal1}, {kX2, kVal2}, {kX3, kVal3}}); + instruction()->Execute(nullptr); + EXPECT_EQ(GetRegisterValue<Reg>(kX3), 0); +} + +} // namespace