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