Import of kelvin-sim using copybara.

Included changes:

  - 812935805 Add ELF program loading to Kelvin cosim DPI. by Shodan Team <no-reply@google.com>
  - 812914930 BEGIN_PUBLIC by Shodan Team <no-reply@google.com>
  - 812914256 Copybara change to remove absl_nonnull and absl_nullable ... by Shodan Team <no-reply@google.com>
  - 811428631 Update copybara to correctly format BUILD files with exte... by Shodan Team <no-reply@google.com>
  - 807862159 Update MPACT-RiscV to latest GH version. by Shodan Team <no-reply@google.com>
  - 805040233 BEGIN_PUBLIC by Shodan Team <no-reply@google.com>
  - 804932065 BEGIN_PUBLIC by Shodan Team <no-reply@google.com>
  - 804640536 BEGIN_PUBLIC by Shodan Team <no-reply@google.com>
  - 800990917 BEGIN_PUBLIC by Shodan Team <no-reply@google.com>
  - 799665499 BEGIN_PUBLIC by Shodan Team <no-reply@google.com>
  - 794735862 Remove protobuf dependency for external GoB setup by Shodan Team <no-reply@google.com>

PiperOrigin-RevId: 812935805
Change-Id: Id034c56599a34e5411d92caad09c755f00623f95
diff --git a/WORKSPACE b/WORKSPACE
index 8fc3a69..caa3e7b 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -6,9 +6,9 @@
 # MPACT-RiscV repo
 http_archive(
     name = "com_google_mpact-riscv",
-    sha256 = "4e24df1e0b41f1ba04c8f72b1abd3d82b71e4517fa2fcd54c103134f535c0db6",
-    strip_prefix = "mpact-riscv-92597b9bc9f07f7dedc0d380af70dbc3cf595339",
-    url = "https://github.com/google/mpact-riscv/archive/92597b9bc9f07f7dedc0d380af70dbc3cf595339.tar.gz",
+    sha256 = "7accadc80be5e3a57723992aafd825d43e29bfeb6646cd111eb3a7134c2d5705",
+    strip_prefix = "mpact-riscv-925b2e06f2820db0012fa78b312a615286ac0911",
+    url = "https://github.com/google/mpact-riscv/archive/925b2e06f2820db0012fa78b312a615286ac0911.tar.gz",
 )
 
 # Download only the single svdpi.h file.
diff --git a/sim/BUILD b/sim/BUILD
index c2c915e..62400ee 100644
--- a/sim/BUILD
+++ b/sim/BUILD
@@ -213,3 +213,101 @@
         "@com_googlesource_code_re2//:re2",
     ],
 )
+
+cc_library(
+    name = "kelvin_v2_state",
+    srcs = [
+        "kelvin_v2_state.cc",
+    ],
+    hdrs = [
+        "kelvin_v2_state.h",
+    ],
+    copts = [
+        "-O3",
+    ],
+    deps = [
+        "@com_google_absl//absl/base:nullability",
+        "@com_google_absl//absl/strings",
+        "@com_google_mpact-riscv//riscv:riscv_state",
+        "@com_google_mpact-sim//mpact/sim/util/memory",
+    ],
+)
+
+mpact_isa_decoder(
+    name = "kelvin_v2_isa",
+    src = "kelvin_v2.isa",
+    includes = [
+        "@com_google_mpact-riscv//riscv:riscv32g.isa",
+        "@com_google_mpact-riscv//riscv:riscv32zb.isa",
+        "@com_google_mpact-riscv//riscv:riscv_vector.isa",
+    ],
+    isa_name = "KelvinV2",
+    deps = [
+        "@com_google_absl//absl/functional:bind_front",
+        "@com_google_mpact-riscv//riscv:riscv_bitmanip_instructions",
+        "@com_google_mpact-riscv//riscv:riscv_g",
+        "@com_google_mpact-riscv//riscv:riscv_v",
+    ],
+)
+
+mpact_bin_fmt_decoder(
+    name = "kelvin_v2_bin_fmt",
+    src = "kelvin_v2.bin_fmt",
+    decoder_name = "KelvinV2",
+    includes = [
+        "@com_google_mpact-riscv//riscv:riscv32g.bin_fmt",
+        "@com_google_mpact-riscv//riscv:riscv_format16.bin_fmt",
+        "@com_google_mpact-riscv//riscv:riscv_format32.bin_fmt",
+        "@com_google_mpact-riscv//riscv:riscv_vector.bin_fmt",
+    ],
+    deps = [
+        ":kelvin_v2_isa",
+    ],
+)
+
+cc_library(
+    name = "kelvin_v2_user_decoder",
+    srcs = [
+        "kelvin_v2_encoding.cc",
+        "kelvin_v2_user_decoder.cc",
+    ],
+    hdrs = [
+        "kelvin_v2_encoding.h",
+        "kelvin_v2_user_decoder.h",
+    ],
+    copts = ["-O3"],
+    deps = [
+        ":kelvin_v2_bin_fmt",
+        ":kelvin_v2_getters",
+        ":kelvin_v2_isa",
+        ":kelvin_v2_state",
+        "@com_google_absl//absl/base:nullability",
+        "@com_google_absl//absl/container:flat_hash_map",
+        "@com_google_absl//absl/functional:any_invocable",
+        "@com_google_mpact-riscv//riscv:riscv_encoding_common",
+        "@com_google_mpact-riscv//riscv:riscv_state",
+        "@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",
+        "@com_google_mpact-sim//mpact/sim/generic:program_error",
+        "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+        "@com_google_mpact-sim//mpact/sim/util/memory",
+    ],
+)
+
+cc_library(
+    name = "kelvin_v2_getters",
+    hdrs = ["kelvin_v2_getters.h"],
+    deps = [
+        "@com_google_absl//absl/base:nullability",
+        "@com_google_absl//absl/container:flat_hash_map",
+        "@com_google_absl//absl/functional:any_invocable",
+        "@com_google_absl//absl/status:statusor",
+        "@com_google_absl//absl/strings",
+        "@com_google_mpact-riscv//riscv:riscv_encoding_common",
+        "@com_google_mpact-riscv//riscv:riscv_getters",
+        "@com_google_mpact-riscv//riscv:riscv_state",
+        "@com_google_mpact-sim//mpact/sim/generic:core",
+        "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+    ],
+)
diff --git a/sim/cosim/BUILD b/sim/cosim/BUILD
index cf69fca..4e00b57 100644
--- a/sim/cosim/BUILD
+++ b/sim/cosim/BUILD
@@ -34,16 +34,18 @@
     ],
     visibility = ["//visibility:public"],
     deps = [
+        "//sim:kelvin_v2_state",
+        "//sim:kelvin_v2_user_decoder",
         "@com_google_absl//absl/log",
         "@com_google_absl//absl/log:check",
+        "@com_google_absl//absl/status",
         "@com_google_absl//absl/strings",
-        "@com_google_mpact-riscv//riscv:riscv32g_vec_decoder",
-        "@com_google_mpact-riscv//riscv:riscv32gv_isa",
         "@com_google_mpact-riscv//riscv:riscv_fp_state",
         "@com_google_mpact-riscv//riscv:riscv_state",
         "@com_google_mpact-riscv//riscv:riscv_top",
         "@com_google_mpact-sim//mpact/sim/generic:core",
         "@com_google_mpact-sim//mpact/sim/util/memory",
+        "@com_google_mpact-sim//mpact/sim/util/program_loader:elf_loader",
     ],
     alwayslink = True,
 )
diff --git a/sim/cosim/kelvin_cosim_dpi.h b/sim/cosim/kelvin_cosim_dpi.h
index 5e1c4db..1830e45 100644
--- a/sim/cosim/kelvin_cosim_dpi.h
+++ b/sim/cosim/kelvin_cosim_dpi.h
@@ -37,6 +37,10 @@
 // Return 0 on success, non-zero on failure.
 int mpact_init();
 
+// Loads an ELF file into the simulation memory.
+// Return 0 on success, non-zero on failure.
+int mpact_load_program(const char* elf_file);
+
 // Reset the MPACT simulator's architectural state.
 // Return 0 on success, non-zero on failure.
 int mpact_reset();
@@ -51,18 +55,11 @@
 // Currently unimplemented and always returns false.
 bool mpact_is_halted();
 
-// Return the current value of the Program Counter (PC).
-// On error, returns 0 and logs an error.
-uint32_t mpact_get_pc();
-
-// Return the value of the specified GPR. GPRs are selected by their index,
-// where 0 is x0, 1 is x1, and so on.
-// On error, returns 0 and logs an error.
-uint32_t mpact_get_gpr(uint32_t index);
-
-// Return the value of the specified CSR. CSRs are selected by their address.
-// On error, returns 0 and logs an error.
-uint32_t mpact_get_csr(uint32_t address);
+// Return the value of the specified register. Register names are provided as
+// null-terminated c-style strings.
+// Returns 0 on success, non-zero on failure.
+// The register value is returned in the uint32_t argument.
+int mpact_get_register(const char* name, uint32_t* value);
 
 // Finalize and clean up MPACT simulator resources.
 // Return 0 on success, non-zero on failure.
diff --git a/sim/cosim/kelvin_cosim_dpi_wrapper.cc b/sim/cosim/kelvin_cosim_dpi_wrapper.cc
index 3a5d7c3..ca1ee22 100644
--- a/sim/cosim/kelvin_cosim_dpi_wrapper.cc
+++ b/sim/cosim/kelvin_cosim_dpi_wrapper.cc
@@ -16,11 +16,12 @@
 #include <memory>
 #include <string>
 
+#include "sim/kelvin_v2_state.h"
+#include "sim/kelvin_v2_user_decoder.h"
 #include "absl/log/check.h"
 #include "absl/log/log.h"
+#include "absl/status/status.h"
 #include "absl/strings/str_cat.h"
-#include "riscv/riscv32g_vec_decoder.h"
-#include "riscv/riscv_csr.h"
 #include "riscv/riscv_fp_state.h"
 #include "riscv/riscv_register.h"
 #include "riscv/riscv_register_aliases.h"
@@ -30,6 +31,7 @@
 #include "mpact/sim/generic/decoder_interface.h"
 #include "mpact/sim/util/memory/flat_demand_memory.h"
 #include "mpact/sim/util/memory/memory_interface.h"
+#include "mpact/sim/util/program_loader/elf_program_loader.h"
 #include "external/svdpi_h_file/file/svdpi.h"
 
 // Include the DPI-C contract header.
@@ -39,9 +41,11 @@
 constexpr uint32_t kKelvinStartAddress = 0;
 
 namespace {
+using ::kelvin::sim::KelvinV2State;
+using ::kelvin::sim::KelvinV2UserDecoder;
 using ::mpact::sim::generic::DecoderInterface;
+using ::mpact::sim::riscv::kFRegisterAliases;
 using ::mpact::sim::riscv::kXRegisterAliases;
-using ::mpact::sim::riscv::RiscV32GVecDecoder;
 using ::mpact::sim::riscv::RiscVFPState;
 using ::mpact::sim::riscv::RiscVState;
 using ::mpact::sim::riscv::RiscVTop;
@@ -49,6 +53,7 @@
 using ::mpact::sim::riscv::RiscVXlen;
 using ::mpact::sim::riscv::RV32Register;
 using ::mpact::sim::riscv::RVFpRegister;
+using ::mpact::sim::util::ElfProgramLoader;
 using ::mpact::sim::util::FlatDemandMemory;
 using ::mpact::sim::util::MemoryInterface;
 
@@ -56,69 +61,85 @@
  public:
   MpactHandle()
       : memory_(std::make_unique<FlatDemandMemory>()),
-        rv_state_(CreateRVState(memory_.get())),
-        rv_fp_state_(CreateFPState(rv_state_.get())),
-        rvv_state_(CreateVectorState(rv_state_.get())),
-        rv_decoder_(CreateDecoder(rv_state_.get(), memory_.get())),
-        rv_top_(CreateRiscVTop(rv_state_.get(), rv_decoder_.get())) {
+        state_(CreateState(memory_.get())),
+        rv_fp_state_(CreateFPState(state_.get())),
+        rvv_state_(CreateVectorState(state_.get())),
+        rv_decoder_(CreateDecoder(state_.get(), memory_.get())),
+        rv_top_(CreateRiscVTop(state_.get(), rv_decoder_.get())),
+        elf_loader_(std::make_unique<ElfProgramLoader>(memory_.get())) {
     absl::Status pc_write = rv_top_->WriteRegister("pc", kKelvinStartAddress);
     CHECK_OK(pc_write) << "Error writing to pc.";
   }
 
+  absl::Status load_program(const std::string& elf_file) {
+    auto load_result = elf_loader_->LoadProgram(elf_file);
+    if (!load_result.ok()) {
+      return absl::InternalError(
+          absl::StrCat("Failed to load program '", elf_file,
+                       "': ", load_result.status().message()));
+    }
+    return absl::OkStatus();
+  }
+
   uint32_t get_pc() {
     absl::StatusOr<uint64_t> read_reg_status = rv_top_->ReadRegister("pc");
     CHECK_OK(read_reg_status);
-    if (!read_reg_status.ok()) {
-      LOG(ERROR) << "[DPI] Failed to read pc.";
-      return 0;
-    }
     return static_cast<uint32_t>(read_reg_status.value());
   }
 
   RiscVTop* rv_top() { return rv_top_.get(); }
 
-  RiscVState* rv_state() { return rv_state_.get(); }
+  KelvinV2State* rv_state() { return state_.get(); }
+
+  ElfProgramLoader* elf_loader() { return elf_loader_.get(); }
 
  private:
-  std::unique_ptr<RiscVState> CreateRVState(MemoryInterface* memory) {
-    auto rv_state =
-        std::make_unique<RiscVState>("RiscV32GV", RiscVXlen::RV32, memory);
+  std::unique_ptr<KelvinV2State> CreateState(MemoryInterface* memory) {
+    auto state =
+        std::make_unique<KelvinV2State>("KelvinV2", RiscVXlen::RV32, memory);
     // Make sure the architectural and abi register aliases are added.
     std::string reg_name;
     for (int i = 0; i < 32; i++) {
       reg_name = absl::StrCat(RiscVState::kXregPrefix, i);
-      (void)rv_state->AddRegister<RV32Register>(reg_name);
-      (void)rv_state->AddRegisterAlias<RV32Register>(reg_name,
-                                                     kXRegisterAliases[i]);
+      [[maybe_unused]] RV32Register* xreg =
+          state->AddRegister<RV32Register>(reg_name);
+      CHECK_OK(state->AddRegisterAlias<RV32Register>(reg_name,
+                                                     kXRegisterAliases[i]));
+
+      reg_name = absl::StrCat(RiscVState::kFregPrefix, i);
+      [[maybe_unused]] RVFpRegister* freg =
+          state->AddRegister<RVFpRegister>(reg_name);
+      CHECK_OK(state->AddRegisterAlias<RVFpRegister>(reg_name,
+                                                     kFRegisterAliases[i]));
     }
-    return rv_state;
+    return state;
   }
 
-  std::unique_ptr<RiscVFPState> CreateFPState(RiscVState* rv_state) {
-    return std::make_unique<RiscVFPState>(rv_state->csr_set(), rv_state);
+  std::unique_ptr<RiscVFPState> CreateFPState(KelvinV2State* state) {
+    return std::make_unique<RiscVFPState>(state->csr_set(), state);
   }
 
-  std::unique_ptr<RiscVVectorState> CreateVectorState(RiscVState* rv_state) {
-    return std::make_unique<RiscVVectorState>(rv_state,
-                                              kKelvinVectorByteLength);
+  std::unique_ptr<RiscVVectorState> CreateVectorState(KelvinV2State* state) {
+    return std::make_unique<RiscVVectorState>(state, kKelvinVectorByteLength);
   }
 
-  std::unique_ptr<DecoderInterface> CreateDecoder(RiscVState* rv_state,
+  std::unique_ptr<DecoderInterface> CreateDecoder(KelvinV2State* state,
                                                   MemoryInterface* memory) {
-    return std::make_unique<RiscV32GVecDecoder>(rv_state, memory);
+    return std::make_unique<KelvinV2UserDecoder>(state, memory);
   }
 
-  std::unique_ptr<RiscVTop> CreateRiscVTop(RiscVState* rv_state,
+  std::unique_ptr<RiscVTop> CreateRiscVTop(KelvinV2State* state,
                                            DecoderInterface* decoder) {
-    return std::make_unique<RiscVTop>("KelvinPlaceholder", rv_state, decoder);
+    return std::make_unique<RiscVTop>("KelvinPlaceholder", state, decoder);
   }
 
   const std::unique_ptr<MemoryInterface> memory_;
-  const std::unique_ptr<RiscVState> rv_state_;
+  const std::unique_ptr<KelvinV2State> state_;
   const std::unique_ptr<RiscVFPState> rv_fp_state_;
   const std::unique_ptr<RiscVVectorState> rvv_state_;
   const std::unique_ptr<DecoderInterface> rv_decoder_;
   const std::unique_ptr<RiscVTop> rv_top_;
+  const std::unique_ptr<ElfProgramLoader> elf_loader_;
 };
 
 MpactHandle* g_mpact_handle = nullptr;
@@ -134,6 +155,20 @@
   return 0;
 }
 
+int mpact_load_program(const char* elf_file) {
+  if (elf_file == nullptr) {
+    LOG(ERROR) << "[DPI] mpact_init: received a null elf program.";
+    return -1;
+  }
+  absl::Status status = g_mpact_handle->load_program(elf_file);
+  if (!status.ok()) {
+    LOG(ERROR) << "[DPI] Failed to load elf program '" << elf_file
+               << "': " << status.message();
+    return -1;
+  }
+  return 0;
+}
+
 int mpact_reset() {
   if (g_mpact_handle != nullptr) {
     mpact_fini();
@@ -171,46 +206,32 @@
   return false;
 }
 
-uint32_t mpact_get_pc() {
-  if (g_mpact_handle == nullptr) {
-    LOG(ERROR) << "[DPI] mpact_get_pc: g_mpact_handle is null.";
-    return 0;
+int mpact_get_register(const char* name, uint32_t* value) {
+  if (value == nullptr) {
+    LOG(ERROR) << "[DPI] mpact_get_register: value is null.";
+    return -1;
   }
-  return g_mpact_handle->get_pc();
-}
-
-uint32_t mpact_get_gpr(uint32_t index) {
-  if (g_mpact_handle == nullptr) {
-    LOG(ERROR) << "[DPI] mpact_get_gpr: g_mpact_handle is null.";
-    return 0;
+  if (name == nullptr) {
+    LOG(ERROR) << "[DPI] mpact_get_register: name is null.";
+    return -3;
   }
-  std::string reg_name =
-      absl::StrCat(mpact::sim::riscv::RiscVState::kXregPrefix, index);
-  mpact::sim::riscv::RiscVTop* rv_top = g_mpact_handle->rv_top();
+  *value = 0;
+  if (g_mpact_handle == nullptr) {
+    LOG(ERROR) << "[DPI] mpact_get_register: g_mpact_handle is null.";
+    return -2;
+  }
+  std::string reg_name(name);
+  RiscVTop* rv_top = g_mpact_handle->rv_top();
   absl::StatusOr<uint64_t> read_reg_status = rv_top->ReadRegister(reg_name);
   if (!read_reg_status.ok()) {
-    LOG(ERROR) << "[DPI] mpact_get_gpr: Failed to read register: " << reg_name;
-    return 0;
+    LOG(ERROR) << "[DPI] mpact_get_register: Failed to read register: "
+               << reg_name;
+    return -3;
   }
-  return static_cast<uint32_t>(read_reg_status.value());
-}
-
-uint32_t mpact_get_csr(uint32_t address) {
-  if (g_mpact_handle == nullptr) {
-    LOG(ERROR) << "[DPI] mpact_get_csr: g_mpact_handle is null.";
-    return 0;
-  }
-  uint64_t csr_index = static_cast<uint64_t>(address);
-
-  absl::StatusOr<mpact::sim::riscv::RiscVCsrInterface*> get_csr_status =
-      g_mpact_handle->rv_state()->csr_set()->GetCsr(csr_index);
-
-  if (!get_csr_status.ok()) {
-    LOG(ERROR) << "[DPI] mpact_get_csr: Failed to get CSR: " << address;
-    return 0;
-  }
-  mpact::sim::riscv::RiscVCsrInterface* csr = get_csr_status.value();
-  return csr->AsUint32();
+  // Kelvin V2 is a 32bit system. RiscVTop::ReadRegister outputs 64bit values
+  // for both 32bit and 64bit systems. We can safely cast the value to uint32_t.
+  *value = static_cast<uint32_t>(*read_reg_status);
+  return 0;
 }
 
 int mpact_fini() {
diff --git a/sim/kelvin_v2.bin_fmt b/sim/kelvin_v2.bin_fmt
new file mode 100644
index 0000000..db59919
--- /dev/null
+++ b/sim/kelvin_v2.bin_fmt
@@ -0,0 +1,799 @@
+// 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.
+
+// Defines the Kelvin V2 instructions for mpact-sim to generate binary decoders.
+decoder KelvinV2 {
+  namespace kelvin::sim::encoding;
+  opcode_enum = "::kelvin::sim::isa32_v2::OpcodeEnum";
+  includes {
+    #include "sim/kelvin_v2_decoder.h"
+  }
+  KelvinV2Inst32 = {
+    RiscVIInst32,
+    RiscVZicsrInst32,
+    RiscVZifenceiInst32,
+    RiscVMInst32,
+    RiscVFInst32,
+    RiscVZve32xInst32,
+    RiscVZbbInst32,
+    RiscVZbbInst32Only,
+    RiscVZbbImmInst32
+  };
+};
+
+// TODO: b/448154052 - Kelvin V2 sim should reuse mpact-riscv isa and bin_fmt
+//                     files.
+instruction group RiscVIInst32 = {RiscVIInstBase32, RiscVIHints32};
+
+
+
+
+format Inst32Format[32] {
+  fields:
+    unsigned bits[25];
+    unsigned opcode[7];
+};
+
+format RType[32] : Inst32Format {
+  fields:
+    unsigned func7[7];
+    unsigned rs2[5];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+  overlays:
+    unsigned r_uimm5[5] = rs2;
+};
+
+// Format for shift immediate for RV64, note 6 bit immediate.
+format RSType[32] : Inst32Format {
+  fields:
+    unsigned func6[6];
+    unsigned r_uimm6[6];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+};
+
+format R4Type[32] : Inst32Format {
+  fields:
+    unsigned rs3[5];
+    unsigned func2[2];
+    unsigned rs2[5];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+};
+
+format IType[32] : Inst32Format {
+  fields:
+    signed imm12[12];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+  overlays:
+    unsigned u_imm12[12] = imm12;
+    unsigned i_uimm5[5] = rs1;
+};
+
+format SType[32] : Inst32Format {
+  fields:
+    unsigned imm7[7];
+    unsigned rs2[5];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned imm5[5];
+    unsigned opcode[7];
+  overlays:
+    signed s_imm[12] = imm7, imm5;
+};
+
+format BType[32] : Inst32Format {
+  fields:
+    unsigned imm7[7];
+    unsigned rs2[5];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned imm5[5];
+    unsigned opcode[7];
+  overlays:
+    signed b_imm[13] = imm7[6], imm5[0], imm7[5..0], imm5[4..1], 0b0;
+};
+
+format UType[32] : Inst32Format {
+  fields:
+    unsigned imm20[20];
+    unsigned rd[5];
+    unsigned opcode[7];
+  overlays:
+    unsigned u_imm[32] = imm20, 0b0000'0000'0000;
+};
+
+format JType[32] : Inst32Format {
+  fields:
+    unsigned imm20[20];
+    unsigned rd[5];
+    unsigned opcode[7];
+  overlays:
+    signed j_imm[21] = imm20[19, 7..0, 8, 18..9], 0b0;
+};
+
+format Fence[32] : Inst32Format {
+  fields:
+    unsigned fm[4];
+    unsigned pred[4];
+    unsigned succ[4];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+};
+
+format AType[32] : Inst32Format {
+  fields:
+    unsigned func5[5];
+    unsigned aq[1];
+    unsigned rl[1];
+    unsigned rs2[5];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+};
+
+format F12Type[32] : Inst32Format {
+  fields:
+    unsigned func12[12];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+};
+
+format ZICBOP[32] : Inst32Format {
+  fields:
+    unsigned offset[7];
+    unsigned func5[5];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned imm5[5];
+    unsigned op[7];
+  overlays:
+    unsigned bop_uimm12[12] = offset, 0b00000;
+}
+
+format MopRType[32] : Inst32Format {
+  fields:
+    unsigned func1[1];
+    unsigned n_hi[1];
+    unsigned func2[2];
+    unsigned n_mid[2];
+    unsigned func4[4];
+    unsigned n_lo[2];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+  overlays:
+    unsigned mop_no[5] = n_hi, n_mid, n_lo;
+};
+
+format MopRRType[32] : Inst32Format {
+  fields:
+    unsigned func1h[1];
+    unsigned n_hi[1];
+    unsigned func2[2];
+    unsigned n_lo[2];
+    unsigned func1l[1];
+    unsigned rs2[5];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+  overlays:
+    unsigned mop_no[3] = n_hi, n_lo;
+};
+
+// Vector instruction formats.
+
+format VMem[32] : Inst32Format {
+  fields:
+    unsigned nf[3];
+    unsigned mew[1];
+    unsigned mop[2];
+    unsigned vm[1];
+    unsigned rs2[5];
+    unsigned rs1[5];
+    unsigned width[3];
+    unsigned vd[5];
+    unsigned opcode[7];
+  overlays:
+    unsigned lumop[5] = rs2;
+    unsigned sumop[5] = rs2;
+    unsigned vs2[5] = rs2;
+    unsigned vs3[5] = vd;
+};
+
+format VArith[32] : Inst32Format {
+  fields:
+    unsigned func6[6];
+    unsigned vm[1];
+    unsigned vs2[5];
+    unsigned vs1[5];
+    unsigned func3[3];
+    unsigned vd[5];
+    unsigned opcode[7];
+  overlays:
+    unsigned uimm5[5] = vs1;
+    unsigned uimm6[6] = func6[0], vs1;
+    unsigned func5[5] = func6[5..1];
+    signed simm5[5] = vs1;
+    unsigned rd[5] = vd;
+    unsigned rs1[5] = vs1;
+    unsigned vd_mask[5] = vd;
+};
+
+format VConfig[32] : Inst32Format {
+  fields:
+    unsigned top12[12];
+    unsigned rs1[5];
+    unsigned func3[3];
+    unsigned rd[5];
+    unsigned opcode[7];
+   overlays:
+    signed zimm11[11] = top12[10..0];
+    unsigned func1[1] = top12[11];
+    unsigned func2[2] = top12[11..10];
+    unsigned func7[7] = top12[11..5];
+    signed zimm10[10] = top12[9..0];
+    unsigned uimm5[5] = rs1;
+    unsigned rs2[5] = top12[4..0];
+};
+
+instruction group RiscVIInstBase32[32] : Inst32Format {
+  // RV32I Base Instruction Set
+  lui    : UType  : opcode == 0b011'0111, rd != 0;
+  auipc  : UType  : opcode == 0b001'0111, rd != 0;
+  jal    : JType  : rd != 0, opcode == 0b110'1111;
+  j      : JType  : rd == 0, opcode == 0b110'1111;
+  jalr   : IType  : rd != 0, func3 == 0b000, opcode == 0b110'0111;
+  jr     : IType  : rd == 0, func3 == 0b000, opcode == 0b110'0111;
+  beq    : BType  : func3 == 0b000, opcode == 0b110'0011;
+  bne    : BType  : func3 == 0b001, opcode == 0b110'0011;
+  blt    : BType  : func3 == 0b100, opcode == 0b110'0011;
+  bge    : BType  : func3 == 0b101, opcode == 0b110'0011;
+  bltu   : BType  : func3 == 0b110, opcode == 0b110'0011;
+  bgeu   : BType  : func3 == 0b111, opcode == 0b110'0011;
+  lb     : BType  : func3 == 0b000, opcode == 0b000'0011;
+  lh     : BType  : func3 == 0b001, opcode == 0b000'0011;
+  lw     : BType  : func3 == 0b010, opcode == 0b000'0011;
+  lbu    : BType  : func3 == 0b100, opcode == 0b000'0011;
+  lhu    : BType  : func3 == 0b101, opcode == 0b000'0011;
+  sb     : SType  : func3 == 0b000, opcode == 0b010'0011;
+  sh     : SType  : func3 == 0b001, opcode == 0b010'0011;
+  sw     : SType  : func3 == 0b010, opcode == 0b010'0011;
+  addi   : IType  : func3 == 0b000, opcode == 0b001'0011, rd != 0;
+  nop    : IType  : func3 == 0b000, opcode == 0b001'0011, rd == 0, imm12 == 0, rs1 == 0;
+  slti   : IType  : func3 == 0b010, opcode == 0b001'0011, rd != 0;
+  sltiu  : IType  : func3 == 0b011, opcode == 0b001'0011, rd != 0;
+  xori   : IType  : func3 == 0b100, rd != 0, opcode == 0b001'0011;
+  ori    : IType  : func3 == 0b110, rd != 0b00000, opcode == 0b001'0011;
+  andi   : IType  : func3 == 0b111, opcode == 0b001'0011, rd != 0;
+  slli   : RType  : func7 == 0b000'0000, func3==0b001, opcode == 0b001'0011;
+  srli   : RType  : func7 == 0b000'0000, func3==0b101, rd != 0, opcode == 0b001'0011;
+  srai   : RType  : func7 == 0b010'0000, func3==0b101, opcode == 0b001'0011;
+  add    : RType  : func7 == 0b000'0000, func3==0b000, rd != 0, opcode == 0b011'0011;
+  sub     : RType : func7 == 0b010'0000, func3==0b000, rd != 0, opcode == 0b011'0011;
+  sll     : RType : func7 == 0b000'0000, func3==0b001, rd != 0, opcode == 0b011'0011;
+  slt     : RType : func7 == 0b000'0000, func3==0b010, rd != 0, opcode == 0b011'0011;
+  sltu    : RType : func7 == 0b000'0000, func3==0b011, rd != 0, opcode == 0b011'0011;
+  xor     : RType : func7 == 0b000'0000, func3==0b100, rd != 0, opcode == 0b011'0011;
+  srl     : RType : func7 == 0b000'0000, func3==0b101, rd != 0, opcode == 0b011'0011;
+  sra     : RType : func7 == 0b010'0000, func3==0b101, rd != 0, opcode == 0b011'0011;
+  or      : RType : func7 == 0b000'0000, func3==0b110, rd != 0, opcode == 0b011'0011;
+  and     : RType : func7 == 0b000'0000, func3==0b111, rd != 0, opcode == 0b011'0011;
+  fence     : Fence : fm == 0b0000, succ != 0b0000, func3 == 0b000, opcode == 0b000'1111;
+  fence_tso : Fence : fm == 0b1000, pred == 0b0011, succ == 0b0011, func3 == 0b000, opcode == 0b000'1111;
+  ecall  : Inst32Format : bits == 0b0000'0000'0000'00000'000'00000, opcode == 0b111'0011;
+  ebreak : Inst32Format : bits == 0b0000'0000'0001'00000'000'00000, opcode == 0b111'0011;
+};
+
+instruction group RiscVZifenceiInst32[32] : Inst32Format {
+  // RV32/RV64 Zifencei Standard Extension
+  fencei : IType  : func3 == 001, opcode == 0b000'1111;
+};
+
+instruction group RiscVZicsrInst32[32] : Inst32Format {
+  // RV32/RV64 Zicsr Standard Extension
+  csrrw    : IType  : func3 == 0b001, u_imm12 != 0b1100'0000'0000, rd != 0,  opcode == 0b111'0011;
+  csrrs    : IType  : func3 == 0b010, rs1 != 0, rd != 0, opcode == 0b111'0011;
+  csrrc    : IType  : func3 == 0b011, rs1 != 0, rd != 0, opcode == 0b111'0011;
+  csrrs_nr : IType  : func3 == 0b010, rs1 != 0, rd == 0, opcode == 0b111'0011;
+  csrrc_nr : IType  : func3 == 0b011, rs1 != 0, rd == 0, opcode == 0b111'0011;
+  csrrw_nr : IType  : func3 == 0b001, u_imm12 != 0b1100'0000'0000, rd == 0,  opcode == 0b111'0011;
+  csrrs_nw : IType  : func3 == 0b010, rs1 == 0, opcode == 0b111'0011;
+  csrrc_nw : IType  : func3 == 0b011, rs1 == 0, opcode == 0b111'0011;
+  csrrwi   : IType  : func3 == 0b101, rd != 0,  opcode == 0b111'0011;
+  csrrsi   : IType  : func3 == 0b110, rs1 != 0, rd != 0, opcode == 0b111'0011;
+  csrrci   : IType  : func3 == 0b111, rs1 != 0, rd != 0, opcode == 0b111'0011;
+  csrrsi_nr: IType  : func3 == 0b110, rs1 != 0, rd == 0, opcode == 0b111'0011;
+  csrrci_nr: IType  : func3 == 0b111, rs1 != 0, rd == 0, opcode == 0b111'0011;
+  csrrwi_nr: IType  : func3 == 0b101, rd == 0,  opcode == 0b111'0011;
+  csrrsi_nw: IType  : func3 == 0b110, rs1 == 0, opcode == 0b111'0011;
+  csrrci_nw: IType  : func3 == 0b111, rs1 == 0, opcode == 0b111'0011;
+  unimp    : IType  : func3 == 0b001, u_imm12 == 0b1100'0000'0000, rs1 == 0, rd == 0, opcode == 0b111'0011;
+};
+
+instruction group RiscVMInst32[32] : Inst32Format {
+  // RV32M Standard Extension
+  mul    : RType  : func7 == 0b000'0001, func3 == 0b000, opcode == 0b011'0011;
+  mulh   : RType  : func7 == 0b000'0001, func3 == 0b001, opcode == 0b011'0011;
+  mulhsu : RType  : func7 == 0b000'0001, func3 == 0b010, opcode == 0b011'0011;
+  mulhu  : RType  : func7 == 0b000'0001, func3 == 0b011, opcode == 0b011'0011;
+  div    : RType  : func7 == 0b000'0001, func3 == 0b100, opcode == 0b011'0011;
+  divu   : RType  : func7 == 0b000'0001, func3 == 0b101, opcode == 0b011'0011;
+  rem    : RType  : func7 == 0b000'0001, func3 == 0b110, opcode == 0b011'0011;
+  remu   : RType  : func7 == 0b000'0001, func3 == 0b111, opcode == 0b011'0011;
+};
+
+instruction group RiscVFInst32[32] : Inst32Format {
+  // RV32F Standard Extension
+  flw      : IType  : func3 == 0b010, opcode == 0b000'0111;
+  fsw      : SType  : func3 == 0b010, opcode == 0b010'0111;
+  fmadd_s  : R4Type : func2 == 0b00,  opcode == 0b100'0011;
+  fmsub_s  : R4Type : func2 == 0b00,  opcode == 0b100'0111;
+  fnmsub_s : R4Type : func2 == 0b00,  opcode == 0b100'1011;
+  fnmadd_s : R4Type : func2 == 0b00,  opcode == 0b100'1111;
+  fadd_s   : RType  : func7 == 0b000'0000, opcode == 0b101'0011;
+  fsub_s   : RType  : func7 == 0b000'0100, opcode == 0b101'0011;
+  fmul_s   : RType  : func7 == 0b000'1000, opcode == 0b101'0011;
+  fdiv_s   : RType  : func7 == 0b000'1100, opcode == 0b101'0011;
+  fsqrt_s  : RType  : func7 == 0b010'1100, rs2 == 0, opcode == 0b101'0011;
+  fsgnj_s  : RType  : func7 == 0b001'0000, func3 == 0b000, opcode == 0b101'0011;
+  fsgnjn_s : RType  : func7 == 0b001'0000, func3 == 0b001, opcode == 0b101'0011;
+  fsgnjx_s : RType  : func7 == 0b001'0000, func3 == 0b010, opcode == 0b101'0011;
+  fmin_s   : RType  : func7 == 0b001'0100, func3 == 0b000, opcode == 0b101'0011;
+  fmax_s   : RType  : func7 == 0b001'0100, func3 == 0b001, opcode == 0b101'0011;
+  fcvt_ws  : RType  : func7 == 0b110'0000, rs2 == 0, opcode == 0b101'0011;
+  fcvt_wus : RType  : func7 == 0b110'0000, rs2 == 1, opcode == 0b101'0011;
+  fmv_xw   : RType  : func7 == 0b111'0000, rs2 == 0, func3 == 0b000, opcode == 0b101'0011;
+  fcmpeq_s : RType  : func7 == 0b101'0000, func3 == 0b010, opcode == 0b101'0011;
+  fcmplt_s : RType  : func7 == 0b101'0000, func3 == 0b001, opcode == 0b101'0011;
+  fcmple_s : RType  : func7 == 0b101'0000, func3 == 0b000, opcode == 0b101'0011;
+  fclass_s : RType  : func7 == 0b111'0000, rs2 == 0, func3 == 0b001, opcode == 0b101'0011;
+  fcvt_sw  : RType  : func7 == 0b110'1000, rs2 == 0, opcode == 0b101'0011;
+  fcvt_swu : RType  : func7 == 0b110'1000, rs2 == 1, opcode == 0b101'0011;
+  fmv_wx   : RType  : func7 == 0b111'1000, rs2 == 0, func3 == 0b000, opcode == 0b101'0011;
+};
+
+// Encoding for RiscV hint instructions.
+instruction group RiscVIHints32[32] : Inst32Format {
+  lui_hint: UType      : opcode == 0b011'0111, rd == 0;
+  auipc_hint: UType    : opcode == 0b001'0111, rd == 0;
+  addi_hint1: IType    : func3 == 0b000, opcode == 0b001'0011, rd == 0, rs1 != 0;
+  addi_hint2: IType    : func3 == 0b000, opcode == 0b001'0011, rd == 0, imm12 != 0;
+  slti_hint : IType    : func3 == 0b010, opcode == 0b001'0011, rd == 0;
+  sltiu_hint: IType    : func3 == 0b011, opcode == 0b001'0011, rd == 0;
+  xori_hint: IType     : func3 == 0b100, rd == 0, opcode == 0b001'0011;
+  ori_hint: IType      : func3 == 0b110, rd == 0b00000, opcode == 0b001'0011;
+  andi_hint: IType     : func3 == 0b111, opcode == 0b001'0011, rd == 0;
+  srli_hint: RType     : func7 == 0b000'0000, func3==0b101, rd == 0, opcode == 0b001'0011;
+  slt_hint: RType      : func7 == 0b000'0000, func3==0b010, rd == 0, opcode == 0b011'0011;
+  slli_semihost: RType : func7 == 0b000'0000, func3==0b001, rd == 0, rs1 == 0, r_uimm5 == 31, opcode == 0b001'0011;
+  slli_hint1: RType    : func7 == 0b000'0000, func3==0b001, rd == 0, rs1 != 0, opcode == 0b001'0011;
+  slli_hint2: RType    : func7 == 0b000'0000, func3==0b001, rd == 0, r_uimm5 != 31, opcode == 0b001'0011;
+  srai_semihost: RType : func7 == 0b010'0000, func3==0b101, rd == 0, rs1 == 0, r_uimm5 == 7, opcode == 0b001'0011;
+  srai_hint1:    RType : func7 == 0b010'0000, func3==0b101, rd == 0, rs1 != 0, opcode == 0b001'0011;
+  srai_hint2:    RType : func7 == 0b010'0000, func3==0b101, rd == 0, r_uimm5 != 7, opcode == 0b001'0011;
+  add_hint1: RType     : func7 == 0b000'0000, func3==0b000, rd == 0, rs1 != 0, opcode == 0b011'0011;
+  add_hint2: RType     : func7 == 0b000'0000, func3==0b000, rd == 0, rs1 == 0, rs2 > 5, opcode == 0b011'0011;
+  add_hint3: RType     : func7 == 0b000'0000, func3==0b000, rd == 0, rs1 == 0, rs2 < 2, opcode == 0b011'0011;
+  sub_hint: RType      : func7 == 0b010'0000, func3==0b000, rd == 0, opcode == 0b011'0011;
+  sll_hint: RType      : func7 == 0b000'0000, func3==0b001, rd == 0, opcode == 0b011'0011;
+  sltu_hint: RType     : func7 == 0b000'0000, func3==0b011, rd == 0, opcode == 0b011'0011;
+  xor_hint: RType      : func7 == 0b000'0000, func3==0b100, rd == 0, opcode == 0b011'0011;
+  srl_hint: RType      : func7 == 0b000'0000, func3==0b101, rd == 0, opcode == 0b011'0011;
+  sra_hint: RType      : func7 == 0b010'0000, func3==0b101, rd == 0, opcode == 0b011'0011;
+  or_hint : RType      : func7 == 0b000'0000, func3==0b110, rd == 0, opcode == 0b011'0011;
+  and_hint: RType      : func7 == 0b000'0000, func3==0b111, rd == 0, opcode == 0b011'0011;
+  fence_hint1: Fence   : func3==0b000, rd == 0, rs1 != 0, fm == 0, pred == 0, opcode == 0b000'1111;
+  fence_hint2: Fence   : func3==0b000, rd == 0, rs1 != 0, fm == 0, succ == 0, opcode == 0b000'1111;
+  fence_hint3: Fence   : func3==0b000, rd != 0, rs1 == 0, fm == 0, pred == 0, opcode == 0b000'1111;
+  fence_hint4: Fence   : func3==0b000, rd != 0, rs1 == 0, fm == 0, succ == 0, opcode == 0b000'1111;
+  fence_hint5: Fence   : func3==0b000, rd == 0, rs1 == 0, fm == 0, pred == 0, succ != 0, opcode == 0b000'1111;
+  fence_hint6: Fence   : func3==0b000, rd == 0, rs1 == 0, fm == 0, succ == 0, opcode == 0b000'1111;
+  fence_hint7: Fence   : func3==0b000, rd != 0, rs1 == 0, fm == 0, pred != 1, succ == 0, opcode == 0b000'1111;
+};
+
+instruction group RiscVZve32xInst32[32] : Inst32Format {
+  //opcfg : VArith : func6 == 0bxxx'xxx, func3 == 0b111, opcode == 0b101'0111;
+  vsetvli_xn  : VConfig : rs1 != 0, func1 == 0, func3 == 0b111, opcode == 0b101'0111;
+  vsetvli_nz  : VConfig : rd != 0, rs1 == 0, func1 == 0, func3 == 0b111, opcode == 0b101'0111;
+  vsetvli_zz  : VConfig : rd == 0, rs1 == 0, func1 == 0, func3 == 0b111, opcode == 0b101'0111;
+  vsetivli    : VConfig : func2 == 0b11, func3 == 0b111, opcode == 0b101'0111;
+  vsetvl_xn   : VConfig : rs1 != 0, func7 == 0b100'0000, func3 == 0b111, opcode == 0b101'0111;
+  vsetvl_nz   : VConfig : rd != 0, rs1 == 0, func7 == 0b100'0000, func3 == 0b111, opcode == 0b101'0111;
+  vsetvl_zz   : VConfig : rd == 0, rs1 == 0, func7 == 0b100'0000, func3 == 0b111, opcode == 0b101'0111;
+
+  // Unit stride, masked (vm=0).
+  vle8      : VMem : vm == 0, nf == 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b000, opcode == 0b000'0111;
+  vle16     : VMem : vm == 0, nf == 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b101, opcode == 0b000'0111;
+  vle32     : VMem : vm == 0, nf == 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b110, opcode == 0b000'0111;
+  // Unit stride, unmasked (vm=1).
+  vle8_vm1  : VMem : vm == 1, nf == 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b000, opcode == 0b000'0111;
+  vle16_vm1 : VMem : vm == 1, nf == 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b101, opcode == 0b000'0111;
+  vle32_vm1 : VMem : vm == 1, nf == 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b110, opcode == 0b000'0111;
+  // Mask load.
+  vlm   : VMem : nf == 0, mew == 0, mop == 0b00, lumop == 0b01011, width == 0b000, opcode == 0b000'0111;
+  // Unit stride, fault first.
+  vle8ff  : VMem : nf == 0, mew == 0, mop == 0b00, lumop == 0b10000, width == 0b000, opcode == 0b000'0111;
+  vle16ff : VMem : nf == 0, mew == 0, mop == 0b00, lumop == 0b10000, width == 0b101, opcode == 0b000'0111;
+  vle32ff : VMem : nf == 0, mew == 0, mop == 0b00, lumop == 0b10000, width == 0b110, opcode == 0b000'0111;
+  // Unit stride, whole register load.
+  vl1re8  : VMem : nf == 0, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b000, opcode == 0b000'0111;
+  vl1re16 : VMem : nf == 0, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b101, opcode == 0b000'0111;
+  vl1re32 : VMem : nf == 0, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b110, opcode == 0b000'0111;
+  vl2re8  : VMem : nf == 1, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b000, opcode == 0b000'0111;
+  vl2re16 : VMem : nf == 1, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b101, opcode == 0b000'0111;
+  vl2re32 : VMem : nf == 1, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b110, opcode == 0b000'0111;
+  vl4re8  : VMem : nf == 3, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b000, opcode == 0b000'0111;
+  vl4re16 : VMem : nf == 3, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b101, opcode == 0b000'0111;
+  vl4re32 : VMem : nf == 3, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b110, opcode == 0b000'0111;
+  vl8re8  : VMem : nf == 7, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b000, opcode == 0b000'0111;
+  vl8re16 : VMem : nf == 7, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b101, opcode == 0b000'0111;
+  vl8re32 : VMem : nf == 7, mop == 0b00, vm == 1, lumop == 0b01000, width == 0b110, opcode == 0b000'0111;
+  // Vector load strided.
+  vlse8  : VMem : nf == 0, mew == 0, mop == 0b10, width == 0b000, opcode == 0b000'0111;
+  vlse16 : VMem : nf == 0, mew == 0, mop == 0b10, width == 0b101, opcode == 0b000'0111;
+  vlse32 : VMem : nf == 0, mew == 0, mop == 0b10, width == 0b110, opcode == 0b000'0111;
+  // Vector load indexed, unordered.
+  vluxei8 : VMem : nf == 0, mew == 0, mop == 0b01, width == 0b000, opcode == 0b000'0111;
+  vluxei16: VMem : nf == 0, mew == 0, mop == 0b01, width == 0b101, opcode == 0b000'0111;
+  vluxei32: VMem : nf == 0, mew == 0, mop == 0b01, width == 0b110, opcode == 0b000'0111;
+  // Vector load indexed, ordered.
+  vloxei8 : VMem : nf == 0, mew == 0, mop == 0b11, width == 0b000, opcode == 0b000'0111;
+  vloxei16: VMem : nf == 0, mew == 0, mop == 0b11, width == 0b101, opcode == 0b000'0111;
+  vloxei32: VMem : nf == 0, mew == 0, mop == 0b11, width == 0b110, opcode == 0b000'0111;
+  // Vector segment load, unit stride.
+  vlsege8:   VMem : nf != 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b000, opcode == 0b000'0111;
+  vlsege16:  VMem : nf != 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b101, opcode == 0b000'0111;
+  vlsege32:  VMem : nf != 0, mew == 0, mop == 0b00, lumop == 0b00000, width == 0b110, opcode == 0b000'0111;
+  // Vector segment load, strided.
+  vlssege8:   VMem : nf != 0, mew == 0, mop == 0b10, width == 0b000, opcode == 0b000'0111;
+  vlssege16:  VMem : nf != 0, mew == 0, mop == 0b10, width == 0b101, opcode == 0b000'0111;
+  vlssege32:  VMem : nf != 0, mew == 0, mop == 0b10, width == 0b110, opcode == 0b000'0111;
+  // Vector segment load, indexed, unordered.
+  vluxsegei8:   VMem : nf != 0, mew == 0, mop == 0b01, width == 0b000, opcode == 0b000'0111;
+  vluxsegei16:  VMem : nf != 0, mew == 0, mop == 0b01, width == 0b101, opcode == 0b000'0111;
+  vluxsegei32:  VMem : nf != 0, mew == 0, mop == 0b01, width == 0b110, opcode == 0b000'0111;
+  // Vector segement load, indexed, ordered.
+  vloxsegei8:   VMem : nf != 0, mew == 0, mop == 0b11, width == 0b000, opcode == 0b000'0111;
+  vloxsegei16:  VMem : nf != 0, mew == 0, mop == 0b11, width == 0b101, opcode == 0b000'0111;
+  vloxsegei32:  VMem : nf != 0, mew == 0, mop == 0b11, width == 0b110, opcode == 0b000'0111;
+
+
+  // VECTOR STORES
+
+  // Unit stride.
+  vse8  : VMem : nf == 0, mew == 0, mop == 0b00, sumop == 0b00000, width == 0b000, opcode == 0b010'0111;
+  vse16 : VMem : nf == 0, mew == 0, mop == 0b00, sumop == 0b00000, width == 0b101, opcode == 0b010'0111;
+  vse32 : VMem : nf == 0, mew == 0, mop == 0b00, sumop == 0b00000, width == 0b110, opcode == 0b010'0111;
+  // Mask store.
+  vsm   : VMem : nf == 0, mew == 0, mop == 0b00, sumop == 0b01011, width == 0b000, opcode == 0b010'0111;
+  // Unit stride, fault first.
+  vse8ff  : VMem : nf == 0, mew == 0, mop == 0b00, sumop == 0b10000, width == 0b000, opcode == 0b010'0111;
+  vse16ff : VMem : nf == 0, mew == 0, mop == 0b00, sumop == 0b10000, width == 0b101, opcode == 0b010'0111;
+  vse32ff : VMem : nf == 0, mew == 0, mop == 0b00, sumop == 0b10000, width == 0b110, opcode == 0b010'0111;
+  // Unit stride, whole register store.
+  vs1re8  : VMem : nf == 0, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b000, opcode == 0b010'0111;
+  vs1re16 : VMem : nf == 0, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b101, opcode == 0b010'0111;
+  vs1re32 : VMem : nf == 0, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b110, opcode == 0b010'0111;
+  vs2re8  : VMem : nf == 1, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b000, opcode == 0b010'0111;
+  vs2re16 : VMem : nf == 1, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b101, opcode == 0b010'0111;
+  vs2re32 : VMem : nf == 1, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b110, opcode == 0b010'0111;
+  vs4re8  : VMem : nf == 3, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b000, opcode == 0b010'0111;
+  vs4re16 : VMem : nf == 3, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b101, opcode == 0b010'0111;
+  vs4re32 : VMem : nf == 3, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b110, opcode == 0b010'0111;
+  vs8re8  : VMem : nf == 7, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b000, opcode == 0b010'0111;
+  vs8re16 : VMem : nf == 7, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b101, opcode == 0b010'0111;
+  vs8re32 : VMem : nf == 7, mop == 0b00, vm == 1, sumop == 0b01000, width == 0b110, opcode == 0b010'0111;
+  // Store strided.
+  vsse8  : VMem : nf == 0, mew == 0, mop == 0b10, width == 0b000, opcode == 0b010'0111;
+  vsse16 : VMem : nf == 0, mew == 0, mop == 0b10, width == 0b101, opcode == 0b010'0111;
+  vsse32 : VMem : nf == 0, mew == 0, mop == 0b10, width == 0b110, opcode == 0b010'0111;
+  // Store indexed, unordered.
+  vsuxei8 : VMem : nf == 0, mew == 0, mop == 0b01, width == 0b000, opcode == 0b010'0111;
+  vsuxei16: VMem : nf == 0, mew == 0, mop == 0b01, width == 0b101, opcode == 0b010'0111;
+  vsuxei32: VMem : nf == 0, mew == 0, mop == 0b01, width == 0b110, opcode == 0b010'0111;
+  // Store indexed, ordered.
+  vsoxei8 : VMem : nf == 0, mew == 0, mop == 0b11, width == 0b000, opcode == 0b010'0111;
+  vsoxei16: VMem : nf == 0, mew == 0, mop == 0b11, width == 0b101, opcode == 0b010'0111;
+  vsoxei32: VMem : nf == 0, mew == 0, mop == 0b11, width == 0b110, opcode == 0b010'0111;
+  // Vector segment store, unit stride.
+  vssege8:   VMem : nf != 0, mew == 0, mop == 0b00, sumop == 0b00000, width == 0b000, opcode == 0b010'0111;
+  vssege16:  VMem : nf != 0, mew == 0, mop == 0b00, sumop == 0b00000, width == 0b101, opcode == 0b010'0111;
+  vssege32:  VMem : nf != 0, mew == 0, mop == 0b00, sumop == 0b00000, width == 0b110, opcode == 0b010'0111;
+  // Vector segment store, strided.
+  vsssege8:   VMem : nf != 0, mew == 0, mop == 0b10, width == 0b000, opcode == 0b010'0111;
+  vsssege16:  VMem : nf != 0, mew == 0, mop == 0b10, width == 0b101, opcode == 0b010'0111;
+  vsssege32:  VMem : nf != 0, mew == 0, mop == 0b10, width == 0b110, opcode == 0b010'0111;
+  // Vector segment store, indexed, unordered.
+  vsuxsegei8:   VMem : nf != 0, mew == 0, mop == 0b01, width == 0b000, opcode == 0b010'0111;
+  vsuxsegei16:  VMem : nf != 0, mew == 0, mop == 0b01, width == 0b101, opcode == 0b010'0111;
+  vsuxsegei32:  VMem : nf != 0, mew == 0, mop == 0b01, width == 0b110, opcode == 0b010'0111;
+  // Vector segement store, indexed, ordered.
+  vsoxsegei8:   VMem : nf != 0, mew == 0, mop == 0b11, width == 0b000, opcode == 0b010'0111;
+  vsoxsegei16:  VMem : nf != 0, mew == 0, mop == 0b11, width == 0b101, opcode == 0b010'0111;
+  vsoxsegei32:  VMem : nf != 0, mew == 0, mop == 0b11, width == 0b110, opcode == 0b010'0111;
+
+  // Integer: OPIVV, OPIVX, OPIVI
+  //opivv : VArith : func6 == 0bxxx'xxx, func3 == 0b000, opcode == 0b101'0111;
+  //opivx : VArith : func6 == 0bxxx'xxx, func3 == 0b100, opcode == 0b101'0111;
+  //opivi : VArith : func6 == 0bxxx'xxx, func3 == 0b011, opcode == 0b101'0111;
+
+  vadd_vv  : VArith : func6 == 0b000'000, func3 == 0b000, opcode == 0b101'0111;
+  vadd_vx  : VArith : func6 == 0b000'000, func3 == 0b100, opcode == 0b101'0111;
+  vadd_vi  : VArith : func6 == 0b000'000, func3 == 0b011, opcode == 0b101'0111;
+  vsub_vv  : VArith : func6 == 0b000'010, func3 == 0b000, opcode == 0b101'0111;
+  vsub_vx  : VArith : func6 == 0b000'010, func3 == 0b100, opcode == 0b101'0111;
+  vrsub_vx : VArith : func6 == 0b000'011, func3 == 0b100, opcode == 0b101'0111;
+  vrsub_vi : VArith : func6 == 0b000'011, func3 == 0b011, opcode == 0b101'0111;
+  vminu_vv : VArith : func6 == 0b000'100, func3 == 0b000, opcode == 0b101'0111;
+  vminu_vx : VArith : func6 == 0b000'100, func3 == 0b100, opcode == 0b101'0111;
+  vmin_vv  : VArith : func6 == 0b000'101, func3 == 0b000, opcode == 0b101'0111;
+  vmin_vx  : VArith : func6 == 0b000'101, func3 == 0b100, opcode == 0b101'0111;
+  vmaxu_vv : VArith : func6 == 0b000'110, func3 == 0b000, opcode == 0b101'0111;
+  vmaxu_vx : VArith : func6 == 0b000'110, func3 == 0b100, opcode == 0b101'0111;
+  vmax_vv  : VArith : func6 == 0b000'111, func3 == 0b000, opcode == 0b101'0111;
+  vmax_vx  : VArith : func6 == 0b000'111, func3 == 0b100, opcode == 0b101'0111;
+  vand_vv  : VArith : func6 == 0b001'001, func3 == 0b000, opcode == 0b101'0111;
+  vand_vx  : VArith : func6 == 0b001'001, func3 == 0b100, opcode == 0b101'0111;
+  vand_vi  : VArith : func6 == 0b001'001, func3 == 0b011, opcode == 0b101'0111;
+  vor_vv   : VArith : func6 == 0b001'010, func3 == 0b000, opcode == 0b101'0111;
+  vor_vx   : VArith : func6 == 0b001'010, func3 == 0b100, opcode == 0b101'0111;
+  vor_vi   : VArith : func6 == 0b001'010, func3 == 0b011, opcode == 0b101'0111;
+  vxor_vv  : VArith : func6 == 0b001'011, func3 == 0b000, opcode == 0b101'0111;
+  vxor_vx  : VArith : func6 == 0b001'011, func3 == 0b100, opcode == 0b101'0111;
+  vxor_vi  : VArith : func6 == 0b001'011, func3 == 0b011, opcode == 0b101'0111;
+  vrgather_vv : VArith : func6 == 0b001'100, func3 == 0b000, opcode == 0b101'0111;
+  vrgather_vx : VArith : func6 == 0b001'100, func3 == 0b100, opcode == 0b101'0111;
+  vrgather_vi : VArith : func6 == 0b001'100, func3 == 0b011, opcode == 0b101'0111;
+  vslideup_vx : VArith : func6 == 0b001'110, func3 == 0b100, opcode == 0b101'0111;
+  vslideup_vi : VArith : func6 == 0b001'110, func3 == 0b011, opcode == 0b101'0111;
+  vrgatherei16_vv : VArith : func6 == 0b001'110, func3 == 0b000, opcode == 0b101'0111;
+  vslidedown_vx : VArith : func6 == 0b001'111, func3 == 0b100, opcode == 0b101'0111;
+  vslidedown_vi : VArith : func6 == 0b001'111, func3 == 0b011, opcode == 0b101'0111;
+  vadc_vv   : VArith : func6 == 0b010'000, vd != 0, vm == 0, func3 == 0b000, opcode == 0b101'0111;
+  vadc_vx   : VArith : func6 == 0b010'000, vd != 0, vm == 0, func3 == 0b100, opcode == 0b101'0111;
+  vadc_vi   : VArith : func6 == 0b010'000, vd != 0, vm == 0, func3 == 0b011, opcode == 0b101'0111;
+  vmadc_vv  : VArith : func6 == 0b010'001, func3 == 0b000, opcode == 0b101'0111;
+  vmadc_vx  : VArith : func6 == 0b010'001, func3 == 0b100, opcode == 0b101'0111;
+  vmadc_vi  : VArith : func6 == 0b010'001, func3 == 0b011, opcode == 0b101'0111;
+  vsbc_vv   : VArith : func6 == 0b010'010, vd != 0, vm == 0, func3 == 0b000, opcode == 0b101'0111;
+  vsbc_vx   : VArith : func6 == 0b010'010, vd != 0, vm == 0, func3 == 0b100, opcode == 0b101'0111;
+  vmsbc_vv  : VArith : func6 == 0b010'011, func3 == 0b000, opcode == 0b101'0111;
+  vmsbc_vx  : VArith : func6 == 0b010'011, func3 == 0b100, opcode == 0b101'0111;
+  vmerge_vv : VArith : func6 == 0b010'111, vm == 0, func3 == 0b000, opcode == 0b101'0111;
+  vmerge_vx : VArith : func6 == 0b010'111, vm == 0, func3 == 0b100, opcode == 0b101'0111;
+  vmerge_vi : VArith : func6 == 0b010'111, vm == 0, func3 == 0b011, opcode == 0b101'0111;
+  vmv_vv    : VArith : func6 == 0b010'111, vm == 1, vs2 == 0, func3 == 0b000, opcode == 0b101'0111;
+  vmv_vx    : VArith : func6 == 0b010'111, vm == 1, vs2 == 0, func3 == 0b100, opcode == 0b101'0111;
+  vmv_vi    : VArith : func6 == 0b010'111, vm == 1, vs2 == 0, func3 == 0b011, opcode == 0b101'0111;
+  vmseq_vv  : VArith : func6 == 0b011'000, func3 == 0b000, opcode == 0b101'0111;
+  vmseq_vx  : VArith : func6 == 0b011'000, func3 == 0b100, opcode == 0b101'0111;
+  vmseq_vi  : VArith : func6 == 0b011'000, func3 == 0b011, opcode == 0b101'0111;
+  vmsne_vv  : VArith : func6 == 0b011'001, func3 == 0b000, opcode == 0b101'0111;
+  vmsne_vx  : VArith : func6 == 0b011'001, func3 == 0b100, opcode == 0b101'0111;
+  vmsne_vi  : VArith : func6 == 0b011'001, func3 == 0b011, opcode == 0b101'0111;
+  vmsltu_vv : VArith : func6 == 0b011'010, func3 == 0b000, opcode == 0b101'0111;
+  vmsltu_vx : VArith : func6 == 0b011'010, func3 == 0b100, opcode == 0b101'0111;
+  vmslt_vv  : VArith : func6 == 0b011'011, func3 == 0b000, opcode == 0b101'0111;
+  vmslt_vx  : VArith : func6 == 0b011'011, func3 == 0b100, opcode == 0b101'0111;
+  vmsleu_vv : VArith : func6 == 0b011'100, func3 == 0b000, opcode == 0b101'0111;
+  vmsleu_vx : VArith : func6 == 0b011'100, func3 == 0b100, opcode == 0b101'0111;
+  vmsleu_vi : VArith : func6 == 0b011'100, func3 == 0b011, opcode == 0b101'0111;
+  vmsle_vv  : VArith : func6 == 0b011'101, func3 == 0b000, opcode == 0b101'0111;
+  vmsle_vx  : VArith : func6 == 0b011'101, func3 == 0b100, opcode == 0b101'0111;
+  vmsle_vi  : VArith : func6 == 0b011'101, func3 == 0b011, opcode == 0b101'0111;
+  vmsgtu_vx : VArith : func6 == 0b011'110, func3 == 0b100, opcode == 0b101'0111;
+  vmsgtu_vi : VArith : func6 == 0b011'110, func3 == 0b011, opcode == 0b101'0111;
+  vmsgt_vx  : VArith : func6 == 0b011'111, func3 == 0b100, opcode == 0b101'0111;
+  vmsgt_vi  : VArith : func6 == 0b011'111, func3 == 0b011, opcode == 0b101'0111;
+  vsaddu_vv : VArith : func6 == 0b100'000, func3 == 0b000, opcode == 0b101'0111;
+  vsaddu_vx : VArith : func6 == 0b100'000, func3 == 0b100, opcode == 0b101'0111;
+  vsaddu_vi : VArith : func6 == 0b100'000, func3 == 0b011, opcode == 0b101'0111;
+  vsadd_vv  : VArith : func6 == 0b100'001, func3 == 0b000, opcode == 0b101'0111;
+  vsadd_vx  : VArith : func6 == 0b100'001, func3 == 0b100, opcode == 0b101'0111;
+  vsadd_vi  : VArith : func6 == 0b100'001, func3 == 0b011, opcode == 0b101'0111;
+  vssubu_vv : VArith : func6 == 0b100'010, func3 == 0b000, opcode == 0b101'0111;
+  vssubu_vx : VArith : func6 == 0b100'010, func3 == 0b100, opcode == 0b101'0111;
+  vssub_vv  : VArith : func6 == 0b100'011, func3 == 0b000, opcode == 0b101'0111;
+  vssub_vx  : VArith : func6 == 0b100'011, func3 == 0b100, opcode == 0b101'0111;
+  vsll_vv   : VArith : func6 == 0b100'101, func3 == 0b000, opcode == 0b101'0111;
+  vsll_vx   : VArith : func6 == 0b100'101, func3 == 0b100, opcode == 0b101'0111;
+  vsll_vi   : VArith : func6 == 0b100'101, func3 == 0b011, opcode == 0b101'0111;
+  vsmul_vv  : VArith : func6 == 0b100'111, func3 == 0b000, opcode == 0b101'0111;
+  vsmul_vx  : VArith : func6 == 0b100'111, func3 == 0b100, opcode == 0b101'0111;
+  vmv1r_vi  : VArith : func6 == 0b100'111, uimm5 == 0, func3 == 0b011, opcode == 0b101'0111;
+  vmv2r_vi  : VArith : func6 == 0b100'111, uimm5 == 1, func3 == 0b011, opcode == 0b101'0111;
+  vmv4r_vi  : VArith : func6 == 0b100'111, uimm5 == 3, func3 == 0b011, opcode == 0b101'0111;
+  vmv8r_vi  : VArith : func6 == 0b100'111, uimm5 == 7, func3 == 0b011, opcode == 0b101'0111;
+  vsrl_vv   : VArith : func6 == 0b101'000, func3 == 0b000, opcode == 0b101'0111;
+  vsrl_vx   : VArith : func6 == 0b101'000, func3 == 0b100, opcode == 0b101'0111;
+  vsrl_vi   : VArith : func6 == 0b101'000, func3 == 0b011, opcode == 0b101'0111;
+  vsra_vv   : VArith : func6 == 0b101'001, func3 == 0b000, opcode == 0b101'0111;
+  vsra_vx   : VArith : func6 == 0b101'001, func3 == 0b100, opcode == 0b101'0111;
+  vsra_vi   : VArith : func6 == 0b101'001, func3 == 0b011, opcode == 0b101'0111;
+  vssrl_vv  : VArith : func6 == 0b101'010, func3 == 0b000, opcode == 0b101'0111;
+  vssrl_vx  : VArith : func6 == 0b101'010, func3 == 0b100, opcode == 0b101'0111;
+  vssrl_vi  : VArith : func6 == 0b101'010, func3 == 0b011, opcode == 0b101'0111;
+  vssra_vv  : VArith : func6 == 0b101'011, func3 == 0b000, opcode == 0b101'0111;
+  vssra_vx  : VArith : func6 == 0b101'011, func3 == 0b100, opcode == 0b101'0111;
+  vssra_vi  : VArith : func6 == 0b101'011, func3 == 0b011, opcode == 0b101'0111;
+  vnsrl_vv  : VArith : func6 == 0b101'100, func3 == 0b000, opcode == 0b101'0111;
+  vnsrl_vx  : VArith : func6 == 0b101'100, func3 == 0b100, opcode == 0b101'0111;
+  vnsrl_vi  : VArith : func6 == 0b101'100, func3 == 0b011, opcode == 0b101'0111;
+  vnsra_vv  : VArith : func6 == 0b101'101, func3 == 0b000, opcode == 0b101'0111;
+  vnsra_vx  : VArith : func6 == 0b101'101, func3 == 0b100, opcode == 0b101'0111;
+  vnsra_vi  : VArith : func6 == 0b101'101, func3 == 0b011, opcode == 0b101'0111;
+  vnclipu_vv : VArith : func6 == 0b101'110, func3 == 0b000, opcode == 0b101'0111;
+  vnclipu_vx : VArith : func6 == 0b101'110, func3 == 0b100, opcode == 0b101'0111;
+  vnclipu_vi : VArith : func6 == 0b101'110, func3 == 0b011, opcode == 0b101'0111;
+  vnclip_vv : VArith : func6 == 0b101'111, func3 == 0b000, opcode == 0b101'0111;
+  vnclip_vx : VArith : func6 == 0b101'111, func3 == 0b100, opcode == 0b101'0111;
+  vnclip_vi : VArith : func6 == 0b101'111, func3 == 0b011, opcode == 0b101'0111;
+  vwredsumu_vv : VArith : func6 == 0b110'000, func3 == 0b000, opcode == 0b101'0111;
+  vwredsum_vv  : VArith : func6 == 0b110'001, func3 == 0b000, opcode == 0b101'0111;
+
+  // Integer: OPMVV, OPMVX
+  //opmvv : VArith : func6 == 0bxxx'xxx, func3 == 0b010, opcode == 0b101'0111;
+  //opmvx : VArith : func6 == 0bxxx'xxx, func3 == 0b110, opcode == 0b101'0111;
+
+  vredsum_vv  : VArith : func6 == 0b000'000, func3 == 0b010, opcode == 0b101'0111;
+  vredand_vv  : VArith : func6 == 0b000'001, func3 == 0b010, opcode == 0b101'0111;
+  vredor_vv   : VArith : func6 == 0b000'010, func3 == 0b010, opcode == 0b101'0111;
+  vredxor_vv  : VArith : func6 == 0b000'011, func3 == 0b010, opcode == 0b101'0111;
+  vredminu_vv : VArith : func6 == 0b000'100, func3 == 0b010, opcode == 0b101'0111;
+  vredmin_vv  : VArith : func6 == 0b000'101, func3 == 0b010, opcode == 0b101'0111;
+  vredmaxu_vv : VArith : func6 == 0b000'110, func3 == 0b010, opcode == 0b101'0111;
+  vredmax_vv  : VArith : func6 == 0b000'111, func3 == 0b010, opcode == 0b101'0111;
+  vaaddu_vv   : VArith : func6 == 0b001'000, func3 == 0b010, opcode == 0b101'0111;
+  vaaddu_vx   : VArith : func6 == 0b001'000, func3 == 0b110, opcode == 0b101'0111;
+  vaadd_vv    : VArith : func6 == 0b001'001, func3 == 0b010, opcode == 0b101'0111;
+  vaadd_vx    : VArith : func6 == 0b001'001, func3 == 0b110, opcode == 0b101'0111;
+  vasubu_vv   : VArith : func6 == 0b001'010, func3 == 0b010, opcode == 0b101'0111;
+  vasubu_vx   : VArith : func6 == 0b001'010, func3 == 0b110, opcode == 0b101'0111;
+  vasub_vv    : VArith : func6 == 0b001'011, func3 == 0b010, opcode == 0b101'0111;
+  vasub_vx    : VArith : func6 == 0b001'011, func3 == 0b110, opcode == 0b101'0111;
+  vslide1up_vx : VArith : func6 == 0b001'110, func3 == 0b110, opcode == 0b101'0111;
+  vslide1down_vx : VArith : func6 == 0b001'111, func3 == 0b110, opcode == 0b101'0111;
+  vcompress_vv : VArith : func6 == 0b010'111, func3 == 0b010, opcode == 0b101'0111;
+  vmandnot_vv  : VArith : func6 == 0b011'000, func3 == 0b010, opcode == 0b101'0111;
+  vmand_vv     : VArith : func6 == 0b011'001, func3 == 0b010, opcode == 0b101'0111;
+  vmor_vv      : VArith : func6 == 0b011'010, func3 == 0b010, opcode == 0b101'0111;
+  vmxor_vv     : VArith : func6 == 0b011'011, func3 == 0b010, opcode == 0b101'0111;
+  vmornot_vv   : VArith : func6 == 0b011'100, func3 == 0b010, opcode == 0b101'0111;
+  vmnand_vv    : VArith : func6 == 0b011'101, func3 == 0b010, opcode == 0b101'0111;
+  vmnor_vv     : VArith : func6 == 0b011'110, func3 == 0b010, opcode == 0b101'0111;
+  vmxnor_vv    : VArith : func6 == 0b011'111, func3 == 0b010, opcode == 0b101'0111;
+
+  vdivu_vv    : VArith : func6 == 0b100'000, func3 == 0b010, opcode == 0b101'0111;
+  vdivu_vx    : VArith : func6 == 0b100'000, func3 == 0b110, opcode == 0b101'0111;
+  vdiv_vv     : VArith : func6 == 0b100'001, func3 == 0b010, opcode == 0b101'0111;
+  vdiv_vx     : VArith : func6 == 0b100'001, func3 == 0b110, opcode == 0b101'0111;
+  vremu_vv    : VArith : func6 == 0b100'010, func3 == 0b010, opcode == 0b101'0111;
+  vremu_vx    : VArith : func6 == 0b100'010, func3 == 0b110, opcode == 0b101'0111;
+  vrem_vv     : VArith : func6 == 0b100'011, func3 == 0b010, opcode == 0b101'0111;
+  vrem_vx     : VArith : func6 == 0b100'011, func3 == 0b110, opcode == 0b101'0111;
+  vmulhu_vv   : VArith : func6 == 0b100'100, func3 == 0b010, opcode == 0b101'0111;
+  vmulhu_vx   : VArith : func6 == 0b100'100, func3 == 0b110, opcode == 0b101'0111;
+  vmul_vv     : VArith : func6 == 0b100'101, func3 == 0b010, opcode == 0b101'0111;
+  vmul_vx     : VArith : func6 == 0b100'101, func3 == 0b110, opcode == 0b101'0111;
+  vmulhsu_vv  : VArith : func6 == 0b100'110, func3 == 0b010, opcode == 0b101'0111;
+  vmulhsu_vx  : VArith : func6 == 0b100'110, func3 == 0b110, opcode == 0b101'0111;
+  vmulh_vv    : VArith : func6 == 0b100'111, func3 == 0b010, opcode == 0b101'0111;
+  vmulh_vx    : VArith : func6 == 0b100'111, func3 == 0b110, opcode == 0b101'0111;
+  vmadd_vv    : VArith : func6 == 0b101'001, func3 == 0b010, opcode == 0b101'0111;
+  vmadd_vx    : VArith : func6 == 0b101'001, func3 == 0b110, opcode == 0b101'0111;
+  vnmsub_vv   : VArith : func6 == 0b101'011, func3 == 0b010, opcode == 0b101'0111;
+  vnmsub_vx   : VArith : func6 == 0b101'011, func3 == 0b110, opcode == 0b101'0111;
+  vmacc_vv    : VArith : func6 == 0b101'101, func3 == 0b010, opcode == 0b101'0111;
+  vmacc_vx    : VArith : func6 == 0b101'101, func3 == 0b110, opcode == 0b101'0111;
+  vnmsac_vv   : VArith : func6 == 0b101'111, func3 == 0b010, opcode == 0b101'0111;
+  vnmsac_vx   : VArith : func6 == 0b101'111, func3 == 0b110, opcode == 0b101'0111;
+  vwaddu_vv   : VArith : func6 == 0b110'000, func3 == 0b010, opcode == 0b101'0111;
+  vwaddu_vx   : VArith : func6 == 0b110'000, func3 == 0b110, opcode == 0b101'0111;
+  vwadd_vv    : VArith : func6 == 0b110'001, func3 == 0b010, opcode == 0b101'0111;
+  vwadd_vx    : VArith : func6 == 0b110'001, func3 == 0b110, opcode == 0b101'0111;
+  vwsubu_vv   : VArith : func6 == 0b110'010, func3 == 0b010, opcode == 0b101'0111;
+  vwsubu_vx   : VArith : func6 == 0b110'010, func3 == 0b110, opcode == 0b101'0111;
+  vwsub_vv    : VArith : func6 == 0b110'011, func3 == 0b010, opcode == 0b101'0111;
+  vwsub_vx    : VArith : func6 == 0b110'011, func3 == 0b110, opcode == 0b101'0111;
+  vwaddu_w_vv : VArith : func6 == 0b110'100, func3 == 0b010, opcode == 0b101'0111;
+  vwaddu_w_vx : VArith : func6 == 0b110'100, func3 == 0b110, opcode == 0b101'0111;
+  vwadd_w_vv  : VArith : func6 == 0b110'101, func3 == 0b010, opcode == 0b101'0111;
+  vwadd_w_vx  : VArith : func6 == 0b110'101, func3 == 0b110, opcode == 0b101'0111;
+  vwsubu_w_vv : VArith : func6 == 0b110'110, func3 == 0b010, opcode == 0b101'0111;
+  vwsubu_w_vx : VArith : func6 == 0b110'110, func3 == 0b110, opcode == 0b101'0111;
+  vwsub_w_vv  : VArith : func6 == 0b110'111, func3 == 0b010, opcode == 0b101'0111;
+  vwsub_w_vx  : VArith : func6 == 0b110'111, func3 == 0b110, opcode == 0b101'0111;
+  vwmulu_vv   : VArith : func6 == 0b111'000, func3 == 0b010, opcode == 0b101'0111;
+  vwmulu_vx   : VArith : func6 == 0b111'000, func3 == 0b110, opcode == 0b101'0111;
+  vwmulsu_vv  : VArith : func6 == 0b111'010, func3 == 0b010, opcode == 0b101'0111;
+  vwmulsu_vx  : VArith : func6 == 0b111'010, func3 == 0b110, opcode == 0b101'0111;
+  vwmul_vv    : VArith : func6 == 0b111'011, func3 == 0b010, opcode == 0b101'0111;
+  vwmul_vx    : VArith : func6 == 0b111'011, func3 == 0b110, opcode == 0b101'0111;
+  vwmaccu_vv  : VArith : func6 == 0b111'100, func3 == 0b010, opcode == 0b101'0111;
+  vwmaccu_vx  : VArith : func6 == 0b111'100, func3 == 0b110, opcode == 0b101'0111;
+  vwmacc_vv   : VArith : func6 == 0b111'101, func3 == 0b010, opcode == 0b101'0111;
+  vwmacc_vx   : VArith : func6 == 0b111'101, func3 == 0b110, opcode == 0b101'0111;
+  vwmaccus_vv : VArith : func6 == 0b111'110, func3 == 0b010, opcode == 0b101'0111;
+  vwmaccus_vx : VArith : func6 == 0b111'110, func3 == 0b110, opcode == 0b101'0111;
+  vwmaccsu_vv : VArith : func6 == 0b111'111, func3 == 0b010, opcode == 0b101'0111;
+  vwmaccsu_vx : VArith : func6 == 0b111'111, func3 == 0b110, opcode == 0b101'0111;
+
+  // VWXUNARY0 vv: VArith : func6 == 0b010'000, func3 == 0b010, opcode == 0b101'0111;
+  vmv_x_s     : VArith : func6 == 0b010'000, vs1 == 0b00000, func3 == 0b010, opcode == 0b101'0111;
+  vcpop       : VArith : func6 == 0b010'000, vs1 == 0b10000, func3 == 0b010, opcode == 0b101'0111;
+  vfirst      : VArith : func6 == 0b010'000, vs1 == 0b10001, func3 == 0b010, opcode == 0b101'0111;
+
+  // VRXUNARY0 vx: VArith : func6 == 0b010'000, func3 == 0b110, opcode == 0b101'0111;
+  vmv_s_x     : VArith : func6 == 0b010'000, vs2 == 0, func3 == 0b110, opcode == 0b101'0111;
+
+  // VXUNARY0 vv : VArith : func6 == 0b010'010, func3 == 0b010, opcode == 0b101'0111;
+  vzext_vf8: VArith : func6 == 0b010'010, vs1 == 0b00010, func3 == 0b010, opcode == 0b101'0111;
+  vsext_vf8: VArith : func6 == 0b010'010, vs1 == 0b00011, func3 == 0b010, opcode == 0b101'0111;
+  vzext_vf4: VArith : func6 == 0b010'010, vs1 == 0b00100, func3 == 0b010, opcode == 0b101'0111;
+  vsext_vf4: VArith : func6 == 0b010'010, vs1 == 0b00101, func3 == 0b010, opcode == 0b101'0111;
+  vzext_vf2: VArith : func6 == 0b010'010, vs1 == 0b00110, func3 == 0b010, opcode == 0b101'0111;
+  vsext_vf2: VArith : func6 == 0b010'010, vs1 == 0b00111, func3 == 0b010, opcode == 0b101'0111;
+
+  // VMUNARY vv  : VArith : func6 == 0b010'100, func3 == 0b010, opcode == 0b101'0111;
+  vmsbf : VArith : func6 == 0b010'100, vs1 == 0b00001, func3 == 0b010, opcode == 0b101'0111;
+  vmsof : VArith : func6 == 0b010'100, vs1 == 0b00010, func3 == 0b010, opcode == 0b101'0111;
+  vmsif : VArith : func6 == 0b010'100, vs1 == 0b00011, func3 == 0b010, opcode == 0b101'0111;
+  viota : VArith : func6 == 0b010'100, vs1 == 0b10000, func3 == 0b010, opcode == 0b101'0111;
+  vid   : VArith : func6 == 0b010'100, vs1 == 0b10001, func3 == 0b010, opcode == 0b101'0111;
+};
+
+instruction group RiscVZbbInst32[32] : Inst32Format {
+  andn:   RType : func7 == 0b010'0000, func3 == 0b111, opcode == 0b011'0011;
+  orn:    RType : func7 == 0b010'0000, func3 == 0b110, opcode == 0b011'0011;
+  xnor:   RType : func7 == 0b010'0000, func3 == 0b100, opcode == 0b011'0011;
+  clz:    RType : func7 == 0b011'0000, rs2 == 0b0'0000, func3 == 0b001, opcode == 0b001'0011;
+  ctz:    RType : func7 == 0b011'0000, rs2 == 0b0'0001, func3 == 0b001, opcode == 0b001'0011;
+  cpop:   RType : func7 == 0b011'0000, rs2 == 0b0'0010, func3 == 0b001, opcode == 0b001'0011;
+  max:    RType : func7 == 0b000'0101, func3 == 0b110, opcode == 0b011'0011;
+  maxu:   RType : func7 == 0b000'0101, func3 == 0b111, opcode == 0b011'0011;
+  min:    RType : func7 == 0b000'0101, func3 == 0b100, opcode == 0b011'0011;
+  minu:   RType : func7 == 0b000'0101, func3 == 0b101, opcode == 0b011'0011;
+  sext_b: RType : func7 == 0b011'0000, rs2 == 0b0'0100, func3 == 0b001, opcode == 0b001'0011;
+  sext_h: RType : func7 == 0b011'0000, rs2 == 0b0'0101, func3 == 0b001, opcode == 0b001'0011;
+  rol:    RType : func7 == 0b011'0000, func3 == 0b001, opcode == 0b011'0011;
+  ror:    RType : func7 == 0b011'0000, func3 == 0b101, opcode == 0b011'0011;
+  orcb:   RType : func7 == 0b001'0100, rs2 == 0b0'0111, func3 == 0b101, opcode == 0b001'0011;
+  rev8:   RType : func7 == 0b011'0100, rs2 == 0b1'1000, func3 == 0b101, opcode == 0b001'0011;
+};
+
+instruction group RiscVZbbInst32Only[32] : Inst32Format {
+  zext_h: RType : func7 == 0b000'0100, rs2 == 0b0'0000, func3 == 0b100, opcode == 0b011'0011;
+}
+
+instruction group RiscVZbbImmInst32[32] : Inst32Format {
+  rori:   RType : func7 == 0b011'0000, func3 == 0b101, opcode == 0b001'0011;
+}
diff --git a/sim/kelvin_v2.isa b/sim/kelvin_v2.isa
new file mode 100644
index 0000000..9943ea6
--- /dev/null
+++ b/sim/kelvin_v2.isa
@@ -0,0 +1,1804 @@
+// 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.
+
+// This file defines the kelvin v2 isa for mpact-sim. For more info on mpact-sim
+// isa format, check: go/mpact-sim-codelabs-riscv-instruction-decoder
+
+// First disasm field is 18 char wide and left justified.
+disasm widths = {-18};
+
+int global_latency = 0;
+
+isa KelvinV2 {
+  namespace kelvin::sim::isa32_v2;
+  slots { kelvin_v2; }
+}
+
+// TODO: b/448154052 - Kelvin V2 sim should reuse mpact-riscv isa and bin_fmt
+//                     files.
+
+// Basic integer ALU instructions, part of the RiscV 32i subset.
+slot riscv32i {
+  includes {
+    #include "external/com_google_mpact-riscv/riscv/riscv_i_instructions.h"
+  }
+  default size = 4;
+  default latency = global_latency;
+  resources TwoOp = { next_pc, rs1 : rd[..rd]};
+  resources ThreeOp = { next_pc, rs1, rs2 : rd[..rd]};
+  opcodes {
+    addi{: rs1, I_imm12 : rd},
+      resources: TwoOp,
+      disasm: "addi", "%rd, %rs1, %I_imm12",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIAdd";
+    nop{},
+      resources: { next_pc },
+      disasm: "nop",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    slti{: rs1, I_imm12 : rd},
+      resources: TwoOp,
+      disasm: "slti", "%rd, %rs1, %I_imm12",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVISlt";
+    sltiu{: rs1, I_imm12 : rd},
+      resources: TwoOp,
+      disasm: "sltiu", "%rd, %rs1, %I_imm12",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVISltu";
+    andi{: rs1, I_imm12 : rd},
+      resources: TwoOp,
+      disasm: "andi", "%rd, %rs1, %I_imm12",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIAnd";
+    ori{: rs1, I_imm12 : rd},
+      resources: TwoOp,
+      disasm: "ori", "%rd, %rs1, %I_imm12",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIOr";
+    xori{: rs1, I_imm12 : rd},
+      resources: TwoOp,
+      disasm: "xori", "%rd, %rs1, %I_imm12",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIXor";
+    slli{: rs1, I_uimm5 : rd},
+      resources: TwoOp,
+      disasm: "slli", "%rd, %rs1, 0x%(I_uimm5:x)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVISll";
+    srli{: rs1, I_uimm5 : rd},
+      resources: TwoOp,
+      disasm: "srli", "%rd  %rs1, 0x%(I_uimm5:x)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVISrl";
+    srai{: rs1, I_uimm5 : rd},
+      resources: TwoOp,
+      disasm: "srai", "%rd, %rs1, 0x%(I_uimm5:x)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVISra";
+    lui{: U_imm20 : rd},
+      resources: { next_pc : rd[0..]},
+      disasm: "lui", "%rd, 0x%(U_imm20:08x)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVILui";
+    auipc{: U_imm20 : rd},
+      resources: { next_pc : rd[0..]},
+      disasm: "auipc", "%rd, 0x%(U_imm20:08x)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIAuipc";
+    add{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "add", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIAdd";
+    slt{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "slt", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVISlt";
+    sltu{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "sltu", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVISltu";
+    and{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "and", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIAnd";
+    or{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "or", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIOr";
+    xor{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "xor", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIXor";
+    sll{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "sll", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVISll";
+    srl{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "srl", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVISrl";
+    sub{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "sub", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVISub";
+    sra{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "sra", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVISra";
+    hint{},
+      disasm: "hint",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    jal{: J_imm20 : next_pc, rd},
+      resources: { next_pc : next_pc[0..], rd[0..]},
+      disasm: "jal", "%rd, 0x%(@+J_imm20:08x)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIJal";
+    jalr{: rs1, J_imm12 : next_pc, rd},
+      resources: { next_pc, rs1 : next_pc[0..], rd[0..]},
+      disasm: "jalr", "%rd, %rs1, %J_imm12",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIJalr";
+    j{: J_imm20 : next_pc, rd},
+      resources: { next_pc : next_pc[0..], rd[0..]},
+      disasm: "j", "0x%(@+J_imm20:08x)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIJal";
+    jr{: rs1, J_imm12 : next_pc, rd},
+      resources: { next_pc, rs1 : next_pc[0..], rd[0..]},
+      disasm: "jr", "%rs1, %J_imm12",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIJalr";
+    beq{: rs1, rs2, B_imm12 : next_pc},
+      resources: { next_pc, rs1, rs2 : next_pc[0..]},
+      disasm: "beq", "%rs1, %rs2, 0x%(@+B_imm12:08x)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIBeq";
+    bne{: rs1, rs2, B_imm12 : next_pc},
+      resources: { next_pc, rs1, rs2 : next_pc[0..]},
+      disasm: "bne", "%rs1, %rs2, 0x%(@+B_imm12:08x)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIBne";
+    blt{: rs1, rs2, B_imm12 : next_pc},
+      resources: { next_pc, rs1, rs2 : next_pc[0..]},
+      disasm: "blt", "%rs1, %rs2, 0x%(@+B_imm12:08x)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIBlt";
+    bltu{: rs1, rs2, B_imm12 : next_pc},
+      resources: { next_pc, rs1, rs2 : next_pc[0..]},
+      disasm: "bltu", "%rs1, %rs2, 0x%(@+B_imm12:08x)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIBltu";
+    bge{: rs1, rs2, B_imm12 : next_pc},
+      resources: { next_pc, rs1, rs2 : next_pc[0..]},
+      disasm: "bge", "%rs1, %rs2, 0x%(@+B_imm12:08x)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIBge";
+    bgeu{: rs1, rs2, B_imm12 : next_pc},
+      resources: { next_pc, rs1, rs2 : next_pc[0..]},
+      disasm: "bgeu", "%rs1, %rs2, 0x%(@+B_imm12:08x)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVIBgeu";
+    lw{(: rs1, I_imm12), (: : rd)},
+      resources: { next_pc, rs1 : rd[0..]},
+      disasm: "lw", "%rd, %I_imm12(%rs1)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVILw",
+               "&::mpact::sim::riscv::RV32::RiscVILwChild";
+    lh{(: rs1, I_imm12 :), (: : rd)},
+      resources: { next_pc, rs1 : rd[0..]},
+      disasm: "lh", "%rd, %I_imm12(%rs1)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVILh",
+               "&::mpact::sim::riscv::RV32::RiscVILhChild";
+    lhu{(: rs1, I_imm12 :), (: : rd)},
+      resources: { next_pc, rs1 : rd[0..]},
+      disasm: "lhu", "%rd, %I_imm12(%rs1)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVILhu",
+               "&::mpact::sim::riscv::RV32::RiscVILhuChild";
+    lb{(: rs1, I_imm12 :), (: : rd)},
+      resources: { next_pc, rs1 : rd[0..]},
+      disasm: "lb", "%rd, %I_imm12(%rs1)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVILb",
+               "&::mpact::sim::riscv::RV32::RiscVILbChild";
+    lbu{(: rs1, I_imm12 :), (: : rd)},
+      resources: { next_pc, rs1 : rd[0..]},
+      disasm: "lbu", "%rd, %I_imm12(%rs1)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVILbu",
+               "&::mpact::sim::riscv::RV32::RiscVILbuChild";
+    sw{: rs1, S_imm12, rs2 : },
+      resources: { next_pc, rs1, rs2 : },
+      disasm: "sw", "%rs2, %S_imm12(%rs1)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVISw";
+    sh{: rs1, S_imm12, rs2 : },
+      resources: { next_pc, rs1, rs2 : },
+      disasm: "sh", "%rs2, %S_imm12(%rs1)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVISh";
+    sb{: rs1, S_imm12, rs2 : },
+      resources: { next_pc, rs1, rs2 : },
+      disasm: "sb", "%rs2, %S_imm12(%rs1)",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVISb";
+    fence{: pred, succ : },
+      disasm: "fence",
+      semfunc: "&::mpact::sim::riscv::RiscVIFence";
+    fence_tso{},
+      disasm: "fence.tso",
+      semfunc: "&::mpact::sim::riscv::RiscVIFenceTso";
+    ecall{},
+      disasm: "ecall",
+      semfunc: "&::mpact::sim::riscv::RiscVIEcall";
+    ebreak{},
+      disasm: "ebreak",
+      semfunc: "&::mpact::sim::riscv::RiscVIEbreak";
+  }
+}
+
+// This slot contains the hint instructions. These execute as nops, but can be
+// changed to provide performance hints to a simulated microarchitecture.
+slot riscv32_hints {
+  includes {
+    #include "external/com_google_mpact-riscv/riscv/riscv_i_instructions.h"
+  }
+  default size = 4;
+  default latency = global_latency;
+  resources TwoOp = { next_pc, rs1 : rd[..rd]};
+  resources ThreeOp = { next_pc, rs1, rs2 : rd[..rd]};
+  opcodes {
+    addi_hint1{: rs1, I_imm12 : rd},
+      resources: TwoOp,
+      disasm: "addi", "%rd, %rs1, %I_imm12",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    addi_hint2{: rs1, I_imm12 : rd},
+      resources: TwoOp,
+      disasm: "addi", "%rd, %rs1, %I_imm12",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    slti_hint{: rs1, I_imm12 : rd},
+      resources: TwoOp,
+      disasm: "slti", "%rd, %rs1, %I_imm12",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    sltiu_hint{: rs1, I_imm12 : rd},
+      resources: TwoOp,
+      disasm: "sltiu", "%rd, %rs1, %I_imm12",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    andi_hint{: rs1, I_imm12 : rd},
+      resources: TwoOp,
+      disasm: "andi", "%rd, %rs1, %I_imm12",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    ori_hint{: rs1, I_imm12 : rd},
+      resources: TwoOp,
+      disasm: "ori", "%rd, %rs1, %I_imm12",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    xori_hint{: rs1, I_imm12 : rd},
+      resources: TwoOp,
+      disasm: "xori", "%rd, %rs1, %I_imm12",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    slli_semihost{: rs1, I_uimm5 : rd},
+      resources: TwoOp,
+      disasm: "slli", "%rd, %rs1, 0x%(I_uimm5:x)",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    slli_hint1{: rs1, I_uimm5 : rd},
+      resources: TwoOp,
+      disasm: "slli", "%rd, %rs1, 0x%(I_uimm5:x)",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    slli_hint2{: rs1, I_uimm5 : rd},
+      resources: TwoOp,
+      disasm: "slli", "%rd, %rs1, 0x%(I_uimm5:x)",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    srli_hint{: rs1, I_uimm5 : rd},
+      resources: TwoOp,
+      disasm: "srli", "%rd  %rs1, 0x%(I_uimm5:x)",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    srai_semihost{: rs1, I_uimm5 : rd},
+      resources: TwoOp,
+      disasm: "srai", "%rd, %rs1, 0x%(I_uimm5:x)",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    srai_hint1{: rs1, I_uimm5 : rd},
+      resources: TwoOp,
+      disasm: "srai", "%rd, %rs1, 0x%(I_uimm5:x)",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    srai_hint2{: rs1, I_uimm5 : rd},
+      resources: TwoOp,
+      disasm: "srai", "%rd, %rs1, 0x%(I_uimm5:x)",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    lui_hint{: U_imm20 : rd},
+      resources: { next_pc : rd[0..]},
+      disasm: "lui", "%rd, 0x%(U_imm20:08x)",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    auipc_hint{: U_imm20 : rd},
+      resources: { next_pc : rd[0..]},
+      disasm: "auipc", "%rd, 0x%(U_imm20:08x)",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    add_hint1{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "add", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    add_hint2{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "add", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    add_hint3{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "add", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    and_hint{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "and", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    or_hint{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "or", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    xor_hint{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "xor", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    sll_hint{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "sll", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    srl_hint{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "srl", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    sub_hint{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "sub", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    sra_hint{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "sra", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    slt_hint{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "slt", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    sltu_hint{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "sltu", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    fence_hint1{: pred, succ :},
+      disasm: "fence",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    fence_hint2{: pred, succ :},
+      disasm: "fence",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    fence_hint3{: pred, succ :},
+      disasm: "fence",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    fence_hint4{: pred, succ :},
+      disasm: "fence",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    fence_hint5{: pred, succ :},
+      disasm: "fence",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    fence_hint6{: pred, succ :},
+      disasm: "fence",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+    fence_hint7{: pred, succ :},
+      disasm: "fence",
+      semfunc: "&::mpact::sim::riscv::RiscVINop";
+  }
+}
+
+
+// Instruction fence.
+slot zfencei {
+  includes {
+    #include "external/com_google_mpact-riscv/riscv/riscv_zfencei_instructions.h"
+  }
+  default size = 4;
+  default latency = global_latency;
+  opcodes {
+    fencei{: I_imm12 : },
+      disasm: "fence.i",
+      semfunc: "&::mpact::sim::riscv::RiscVZFencei";
+  }
+}
+
+// RiscV32 multiply/divide instructions.
+slot riscv32m {
+  includes {
+    #include "external/com_google_mpact-riscv/riscv/riscv_m_instructions.h"
+  }
+  default size = 4;
+  default latency = global_latency;
+  resources ThreeOp = { next_pc, rs1, rs2 : rd[..rd]};
+  opcodes {
+    mul{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "mul", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::MMul";
+    mulh{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "mulh", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::MMulh";
+    mulhu{: rs1, rs2: rd},
+      resources: ThreeOp,
+      disasm: "mulhu", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::MMulhu";
+    mulhsu{: rs1, rs2: rd},
+      resources: ThreeOp,
+      disasm: "mulhsu", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::MMulhsu";
+    div{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "div", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::MDiv";
+    divu{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "divu", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::MDivu";
+    rem{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "rem", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::MRem";
+    remu{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "remu", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::MRemu";
+  }
+}
+
+
+// RiscV32 CSR manipulation instructions.
+slot zicsr {
+  includes {
+    #include "external/com_google_mpact-riscv/riscv/riscv_zicsr_instructions.h"
+  }
+  default size = 4;
+  default latency = global_latency;
+  opcodes {
+    csrrw{: rs1, csr : rd, csr},
+      resources: { next_pc, rs1, csr : rd[0..], csr[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrw",
+      disasm: "csrw", "%rd, %csr, %rs1";
+    csrrs{: rs1, csr : rd, csr},
+      resources: { next_pc, rs1, csr : rd[0..], csr[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrs",
+      disasm: "csrs", "%rd, %csr, %rs1";
+    csrrc{: rs1, csr : rd, csr},
+      resources: { next_pc, rs1, csr : rd[0..], csr[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrc",
+      disasm: "csrc", "%rd, %csr, %rs1";
+    csrrs_nr{: rs1, csr : rd, csr},
+      resources: { next_pc, rs1, csr : rd[0..], csr[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrs",
+      disasm: "csrs", "%csr, %rs1";
+    csrrc_nr{: rs1, csr : rd, csr},
+      resources: { next_pc, rs1, csr : rd[0..], csr[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrc",
+      disasm: "csrc", "%csr, %rs1";
+    csrrw_nr{: rs1, csr : csr},
+      resources: { next_pc, rs1: csr[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrwNr", // rd == 0 (x0).
+      disasm: "csrw", "%csr, %rs1";
+    csrrs_nw{: csr : rd},
+      resources: { next_pc, csr: rd[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrNw", // rs1 == 0 (x0).
+      disasm: "csrs", "%rd, %csr";
+    csrrc_nw{: csr : rd},
+      resources: { next_pc, csr: rd[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrNw", // rs1 == 0 (x0).
+      disasm: "csrc", "%rd, %csr";
+    csrrwi{: CSR_uimm5, csr : rd, csr},
+      resources: { next_pc, csr: rd[0..], csr[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrw",
+      disasm: "csrwi", "%rd, %csr, %CSR_uimm5";
+    csrrsi{: CSR_uimm5, csr : rd, csr},
+      resources: { next_pc, csr: rd[0..], csr[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrs",
+      disasm: "csrsi", "%rd, %csr, %CSR_uimm5";
+    csrrci{: CSR_uimm5, csr : rd, csr},
+      resources: { next_pc, csr: rd[0..], csr[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrc",
+      disasm: "csrci", "%rd, %csr, %CSR_uimm5";
+    csrrsi_nr{: CSR_uimm5, csr : rd, csr},
+      resources: { next_pc, csr: rd[0..], csr[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrs",
+      disasm: "csrsi", "%csr, %CSR_uimm5";
+    csrrci_nr{: CSR_uimm5, csr : rd, csr},
+      resources: { next_pc, csr: rd[0..], csr[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrc",
+      disasm: "csrci", "%csr, %CSR_uimm5";
+    csrrwi_nr{: CSR_uimm5, csr : csr},
+      resources: { next_pc : csr[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrwNr",  // rd == 0 (x0).
+      disasm: "csrrwi", "%csr, %CSR_uimm5";
+    csrrsi_nw{: csr : rd},
+      resources: { next_pc, csr : rd[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrNw", // uimm5 == 0.
+      disasm: "csrsi", "%rd, %csr, 0";
+    csrrci_nw{: csr : rd},
+      resources: { next_pc, csr : rd[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZiCsrrNw", // uimm5 == 0.
+      disasm: "csrwi", "%rd, %csr, 0";
+    unimp{},
+      disasm: "unimp",
+      semfunc: "&::mpact::sim::riscv::RiscVIUnimplemented";
+  }
+}
+
+// RiscV32 F (single precision floating point) instructions.
+slot riscv32f {
+  includes {
+    #include "external/com_google_mpact-riscv/riscv/riscv_f_instructions.h"
+  }
+  default size = 4;
+  default latency = global_latency;
+  resources TwoOp = { next_pc, frs1 : frd[0..]};
+  resources ThreeOp = { next_pc, frs1, frs2 : frd[0..]};
+  resources FourOp = { next_pc, frs1, frs2, frs3 : frd[0..]};
+  opcodes {
+    flw{(: rs1, I_imm12 : ), (: : frd)},
+      resources: { next_pc, rs1 : frd[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVILw",
+               "&::mpact::sim::riscv::RiscVIFlwChild",
+      disasm: "flw", "%frd, %I_imm12(%rs1)";
+    fsw{: rs1, S_imm12, frs2},
+      resources: { next_pc, rs1, frs2},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVFSw",
+      disasm: "fsw", "%frs2, %S_imm12(%rs1)";
+    fadd_s{: frs1, frs2, rm : frd},
+      resources: ThreeOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFAdd",
+      disasm: "fadd", "%frd, %frs1, %frs2";
+    fsub_s{: frs1, frs2, rm : frd},
+      resources: ThreeOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFSub",
+      disasm: "fsub", "%frd, %frs1, %frs2";
+    fmul_s{: frs1, frs2, rm : frd},
+      resources: ThreeOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFMul",
+      disasm: "fmul", "%frd, %frs1, %frs2";
+    fdiv_s{: frs1, frs2, rm : frd},
+      resources: ThreeOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFDiv",
+      disasm: "fdiv", "%frd, %frs1, %frs2";
+    fsqrt_s{: frs1, rm : frd, fflags},
+      resources: TwoOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFSqrt",
+      disasm: "fsqrt", "%frd, %frs1";
+    fmin_s{: frs1, frs2 : frd, fflags},
+      resources: ThreeOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFMin",
+      disasm: "fmin", "%frd, %frs1, %frs2";
+    fmax_s{: frs1, frs2 : frd, fflags},
+      resources: ThreeOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFMax",
+      disasm: "fmax", "%frd, %frs1, %frs2";
+    fmadd_s{: frs1, frs2, frs3, rm : frd, fflags},
+      resources: FourOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFMadd",
+      disasm: "fmadd", "%frd, %frs1, %frs2, %frs3";
+    fmsub_s{: frs1, frs2, frs3, rm : frd, fflags},
+      resources: FourOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFMsub",
+      disasm: "fmsub", "%frd, %frs1, %frs2, %frs3";
+    fnmadd_s{: frs1, frs2, frs3, rm : frd, fflags},
+      resources: FourOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFNmadd",
+      disasm: "fnmadd", "%frd, %frs1, %frs2, %frs3";
+    fnmsub_s{: frs1, frs2, frs3, rm : frd, fflags},
+      resources: FourOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFNmsub",
+      disasm: "fnmsub", "%frd, %frs1, %frs2, %frs3";
+    fcvt_ws{: frs1, rm : rd, fflags},
+      resources: TwoOp,
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVFCvtWs",
+      disasm: "fcvt.w.s", "%rd, %frs1";
+    fcvt_sw{: rs1, rm : frd},
+      resources: TwoOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFCvtSw",
+      disasm: "fcvt.s.w", "%frd, %rs1";
+    fcvt_wus{: frs1, rm : rd, fflags},
+      resources: TwoOp,
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVFCvtWus",
+      disasm: "fcvt.wu.s", "%rd, %frs1";
+    fcvt_swu{: rs1, rm : frd},
+      resources: TwoOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFCvtSwu",
+      disasm: "fcvt.s.wu", "%frd, %rs1";
+    fsgnj_s{: frs1, frs2 : frd},
+      resources: ThreeOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFSgnj",
+      disasm: "fsgn.s", "%frd, %frs1, %frs2";
+    fsgnjn_s{: frs1, frs2 : frd},
+      resources: ThreeOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFSgnjn",
+      disasm: "fsgnjx.s", "%frd, %frs1, %frs2";
+    fsgnjx_s{: frs1, frs2 : frd},
+      resources: ThreeOp,
+      semfunc: "&::mpact::sim::riscv::RiscVFSgnjx",
+      disasm: "fsgnjx.s", "%frd, %frs1, %frs2";
+    fmv_xw{: frs1 : rd},
+      resources: { next_pc, frs1 : rd[0..]},
+      disasm: "mv.x.w", "%rd, %frs1",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVFMvxw";
+    fmv_wx{: rs1 : frd},
+      resources: { next_pc, rs1 : frd[0..]},
+      disasm: "mv.w.x", "%frd, %rs1",
+      semfunc: "&::mpact::sim::riscv::RiscVFMvwx";
+    fcmpeq_s{: frs1, frs2 : rd, fflags},
+      resources: { next_pc, frs1, frs2 : rd[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVFCmpeq",
+      disasm: "fcmpeq", "%rd, %frs1, %frs2";
+    fcmplt_s{: frs1, frs2 : rd, fflags},
+      resources: { next_pc, frs1, frs2 : rd[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVFCmplt",
+      disasm: "fcmplt", "%rd, %frs1, %frs2";
+    fcmple_s{: frs1, frs2 : rd, fflags},
+      resources: { next_pc, frs1, frs2 : rd[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVFCmple",
+      disasm: "fcmple", "%rd, %frs1, %frs2";
+    fclass_s{: frs1 : rd},
+      resources: { next_pc, frs1 : rd[0..]},
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVFClass",
+      disasm: "fclass", "%rd, %frs1";
+  }
+}
+
+
+// Privileged instructions.
+slot privileged {
+  includes {
+    #include "external/com_google_mpact-riscv/riscv/riscv_priv_instructions.h"
+  }
+  default size = 4;
+  default latency = global_latency;
+  opcodes {
+    uret{: : next_pc(0)},
+      disasm: "uret",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVPrivURet";
+    sret{: : next_pc(0)},
+      disasm: "sret",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVPrivSRet";
+    mret{: : next_pc(0)},
+      disasm: "mret",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVPrivMRet";
+    wfi{},
+      disasm: "wfi",
+      semfunc: "&::mpact::sim::riscv::RiscVPrivWfi";
+    // The sfence instruction has 4 behaviors depending on if rs1 and/or rs2
+    // are 0. These behaviors are split into 4 instructions.
+    sfence_vma_zz{: rs1, rs2},
+      resources: {},
+      disasm: "sfence.vma", "%rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVPrivSFenceVmaZZ";
+    sfence_vma_zn{: rs1, rs2},
+      resources: {rs2},
+      disasm: "sfence.vma", "%rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVPrivSFenceVmaZN";
+    sfence_vma_nz{: rs1, rs2},
+      resources: { rs1 },
+      disasm: "sfence.vma", "%rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVPrivSFenceVmaNZ";
+    sfence_vma_nn{: rs1, rs2},
+      resources: {rs1, rs2},
+      disasm: "sfence.vma", "%rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RiscVPrivSFenceVmaNN";
+    // Skipping hypervisor memory management instructions for now.
+  }
+}
+
+slot riscv32_zbb {
+  default size = 4;
+  resources TwoOp = { next_pc, rs1 : rd[..rd]};
+  resources ThreeOp = { next_pc, rs1, rs2 : rd[..rd]};
+  includes {
+    #include "external/com_google_mpact-riscv/riscv/riscv_bitmanip_instructions.h"
+  }
+
+  opcodes {
+    // Logical with negate.
+    andn{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "andn", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVAndn";
+    orn{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "orn", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVOrn";
+    xnor{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "xor", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVXnor";
+    // Counte leading/trailing zero bits.
+    clz{: rs1 : rd},
+      resources: TwoOp,
+      disasm: "clz", "%rd, %rs1",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVClz";
+    ctz{: rs1 : rd},
+      resources: TwoOp,
+      disasm: "ctz", "%rd, %rs1",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVCtz";
+    // Count population
+    cpop{: rs1 : rd},
+      resources: TwoOp,
+      disasm: "cpop", "%rd, %rs1",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVCpop";
+    // Integer minimum/maximum.
+    max{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "max", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVMax";
+    maxu{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "max", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVMaxu";
+    min{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "min", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVMin";
+    minu{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "min", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVMinu";
+    // Sign and zero extension.
+    sext_b{: rs1 : rd},
+      resources: TwoOp,
+      disasm: "sext.b", "%rd, %rs1",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVSextB";
+    sext_h{: rs1 : rd},
+      resources: TwoOp,
+      disasm: "sext.h", "%rd, %rs1",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVSextH";
+    zext_h{: rs1 : rd},
+      resources: TwoOp,
+      disasm: "zext.h", "%rd, %rs1",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVZextH";
+    // Bitwise rotation.
+    rol{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "rol", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVRol";
+    ror{: rs1, rs2 : rd},
+      resources: ThreeOp,
+      disasm: "ror", "%rd, %rs1, %rs2",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVRor";
+    // OR combine.
+    orcb{: rs1 : rd},
+      resources: TwoOp,
+      disasm: "orc", "%rd, %rs1",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVOrcb";
+    // Byte reverse.
+    rev8{: rs1 : rd},
+      resources: TwoOp,
+      disasm: "rev8", "%rd, %rs1",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVRev8";
+  }
+}
+
+slot riscv32_zbb_imm {
+  default size = 4;
+  resources TwoOp = { next_pc, rs1 : rd[..rd]};
+  resources ThreeOp = { next_pc, rs1, rs2 : rd[..rd]};
+  opcodes {
+    rori{: rs1, r_uimm5 : rd},
+      resources: TwoOp,
+      disasm: "rori", "%rd, %rs1, %r_uimm5",
+      semfunc: "&::mpact::sim::riscv::RV32::RiscVRor";
+  }
+}
+
+slot riscv_zve32x {
+  includes {
+    #include "external/com_google_mpact-riscv/riscv/riscv_vector_memory_instructions.h"
+    #include "external/com_google_mpact-riscv/riscv/riscv_vector_opi_instructions.h"
+    #include "external/com_google_mpact-riscv/riscv/riscv_vector_opm_instructions.h"
+    #include "external/com_google_mpact-riscv/riscv/riscv_vector_permute_instructions.h"
+    #include "external/com_google_mpact-riscv/riscv/riscv_vector_reduction_instructions.h"
+    #include "external/com_google_mpact-riscv/riscv/riscv_vector_unary_instructions.h"
+    #include "absl/functional/bind_front.h"
+  }
+  default size = 4;
+  default latency = 0;
+  default opcode =
+    disasm: "Unimplemented instruction at 0x%(@:08x)",
+    semfunc: "&RV32VUnimplementedInstruction";
+  opcodes {
+    // Configuration.
+    vsetvli_xn{: rs1, zimm11: rd},
+      disasm: "vsetvli","%rd,", "%rs1, %zimm11",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::Vsetvl, /*rd_zero*/ false, /*rs1_zero*/ false)";
+    vsetvli_nz{: rs1, zimm11: rd},
+      disasm: "vsetvli", "%rd, %rs1, %zimm11",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::Vsetvl, /*rd_zero*/false, /*rs1_zero*/ true)";
+    vsetvli_zz{: rs1, zimm11: rd},
+      disasm: "vsetvli", "%rd, %rs1, %zimm11",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::Vsetvl, /*rd_zero*/true, /*rs1_zero*/ true)";
+    vsetivli{: uimm5, zimm10: rd},
+      disasm: "vsetivli %uimm5, %zimm10",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::Vsetvl, /*rd_zero*/false, /*rs1_zero*/ false)";
+    vsetvl_xn{: rs1, rs2: rd},
+      disasm: "vsetvl", "%rd, %rs1, %rs2",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::Vsetvl, /*rd_zero*/false, /*rs1_zero*/ false)";
+    vsetvl_nz{: rs1, rs2: rd},
+      disasm: "vsetvl", "%rd, %rs1, %rs2",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::Vsetvl, /*rd_zero*/false, /*rs1_zero*/ true)";
+    vsetvl_zz{: rs1, rs2: rd},
+      disasm: "vsetvl", "%rd, %rs1, %rs2",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::Vsetvl, /*rd_zero*/true, /*rs1_zero*/ true)";
+
+    // VECTOR LOADS
+
+    // Unit stride loads, masked (vm=0)
+    vle8{(: rs1, const1, vmask :), (: : vd )},
+      disasm: "vle8.v", "%vd, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlStrided, /*element_width*/ 1)",
+               "&::mpact::sim::riscv::VlChild";
+    vle16{(: rs1, const2, vmask :), (: : vd )},
+      disasm: "vle16.v", "%vd, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlStrided, /*element_width*/ 2)",
+               "&::mpact::sim::riscv::VlChild";
+    vle32{(: rs1, const4, vmask :), ( : : vd) },
+      disasm: "vle32.v", "%vd, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlStrided, /*element_width*/ 4)",
+               "&::mpact::sim::riscv::VlChild";
+
+    // Unit stride loads, unmasked (vm=1)
+    vle8_vm1{(: rs1, const1, vmask_true :), (: : vd )},
+      disasm: "vle8.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlStrided, /*element_width*/ 1)",
+               "&::mpact::sim::riscv::VlChild";
+    vle16_vm1{(: rs1, const2, vmask_true :), (: : vd )},
+      disasm: "vle16.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlStrided, /*element_width*/ 2)",
+               "&::mpact::sim::riscv::VlChild";
+    vle32_vm1{(: rs1, const4, vmask_true :), ( : : vd) },
+      disasm: "vle32.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlStrided, /*element_width*/ 4)",
+               "&::mpact::sim::riscv::VlChild";
+
+    // Vector strided loads
+    vlse8{(: rs1, rs2, vmask :), (: : vd)},
+      disasm: "vlse8.v", "%vd, (%rs1), %rs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlStrided, /*element_width*/ 1)",
+               "&::mpact::sim::riscv::VlChild";
+    vlse16{(: rs1, rs2, vmask :), (: : vd)},
+      disasm: "vlse16.v", "%vd, (%rs1), %rs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlStrided, /*element_width*/ 2)",
+               "&::mpact::sim::riscv::VlChild";
+    vlse32{(: rs1, rs2, vmask :), (: : vd)},
+      disasm: "vlse32.v", "%vd, (%rs1), %rs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlStrided, /*element_width*/ 4)",
+               "&::mpact::sim::riscv::VlChild";
+
+    // Vector mask load
+    vlm{(: rs1, const1, vmask_true :), (: : vd)},
+      disasm: "vlm.v", "%vd, (%rs1)",
+      semfunc: "&::mpact::sim::riscv::Vlm", "&::mpact::sim::riscv::VlChild";
+
+    // Unit stride vector load, fault first
+    vle8ff{(: rs1, const1, vmask:), (: : vd)},
+      disasm: "vle8ff.v", "%vd, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlStrided, /*element_width*/ 1)",
+               "&::mpact::sim::riscv::VlChild";
+    vle16ff{(: rs1, const2, vmask:), (: : vd)},
+      disasm: "vle16ff.v", "%vd, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlStrided, /*element_width*/ 2)",
+               "&::mpact::sim::riscv::VlChild";
+    vle32ff{(: rs1, const4, vmask:), (: : vd)},
+      disasm: "vle32ff.v", "%vd, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlStrided, /*element_width*/ 4)",
+               "&::mpact::sim::riscv::VlChild";
+
+    // Vector register load
+    vl1re8{(: rs1 :), (: : vd)},
+      disasm: "vl1re8.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlRegister, /*num_regs*/ 1, /*element_width*/ 1)",
+               "&::mpact::sim::riscv::VlChild";
+    vl1re16{(: rs1 :), (: : vd)},
+      disasm: "vl1re16.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlRegister, /*num_regs*/ 1, /*element_width*/ 2)",
+               "&::mpact::sim::riscv::VlChild";
+    vl1re32{(: rs1 :), (: : vd)},
+      disasm: "vl1re32.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlRegister, /*num_regs*/ 1, /*element_width*/ 4)",
+               "&::mpact::sim::riscv::VlChild";
+    vl2re8{(: rs1 :), (: : vd)},
+      disasm: "vl2re8.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlRegister, /*num_regs*/ 2, /*element_width*/ 1)",
+               "&::mpact::sim::riscv::VlChild";
+    vl2re16{(: rs1 :), (: : vd)},
+      disasm: "vl2re16.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlRegister, /*num_regs*/ 2, /*element_width*/ 2)",
+               "&::mpact::sim::riscv::VlChild";
+    vl2re32{(: rs1 :), (: : vd)},
+      disasm: "vl2re32.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlRegister, /*num_regs*/ 2, /*element_width*/ 4)",
+               "&::mpact::sim::riscv::VlChild";
+    vl4re8{(: rs1 :), (: : vd)},
+      disasm: "vl4re8.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlRegister, /*num_regs*/ 4, /*element_width*/ 1)",
+               "&::mpact::sim::riscv::VlChild";
+    vl4re16{(: rs1 :), (: : vd)},
+      disasm: "vl4re16.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlRegister, /*num_regs*/ 4, /*element_width*/ 2)",
+               "&::mpact::sim::riscv::VlChild";
+    vl4re32{(: rs1 :), (: : vd)},
+      disasm: "vl4re32.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlRegister, /*num_regs*/ 4, /*element_width*/ 4)",
+               "&::mpact::sim::riscv::VlChild";
+    vl8re8{(: rs1 :), (: : vd)},
+      disasm: "vl8re8.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlRegister, /*num_regs*/ 8, /*element_width*/ 1)",
+               "&::mpact::sim::riscv::VlChild";
+    vl8re16{(: rs1 :), (: : vd)},
+      disasm: "vl8re16.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlRegister, /*num_regs*/ 8, /*element_width*/ 2)",
+               "&::mpact::sim::riscv::VlChild";
+    vl8re32{(: rs1 :), (: : vd)},
+      disasm: "vl8re32.v", "%vd, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlRegister, /*num_regs*/ 8, /*element_width*/ 4)",
+               "&::mpact::sim::riscv::VlChild";
+
+    // Vector load, indexed, unordered.
+    vluxei8{(: rs1, vs2, vmask:), (: : vd)},
+      disasm: "vluxei8.v", "%vd, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlIndexed, /*index_width*/ 1)",
+               "&::mpact::sim::riscv::VlChild";
+    vluxei16{(: rs1, vs2, vmask:), (: : vd)},
+      disasm: "vluxei16.v", "%vd, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlIndexed, /*index_width*/ 2)",
+               "&::mpact::sim::riscv::VlChild";
+    vluxei32{(: rs1, vs2, vmask:), (: : vd)},
+      disasm: "vluxei32.v", "%vd, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlIndexed, /*index_width*/ 4)",
+               "&::mpact::sim::riscv::VlChild";
+
+    // Vector load, indexed, ordered.
+    vloxei8{(: rs1, vs2, vmask:), (: : vd)},
+      disasm: "vloxei8.v", "%vd, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlIndexed, /*index_width*/ 1)",
+               "&::mpact::sim::riscv::VlChild";
+    vloxei16{(: rs1, vs2, vmask:), (: : vd)},
+      disasm: "vloxei16.v", "%vd, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlIndexed, /*index_width*/ 2)",
+               "&::mpact::sim::riscv::VlChild";
+    vloxei32{(: rs1, vs2, vmask:), (: : vd)},
+      disasm: "vloxei32.v", "%vd, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlIndexed, /*index_width*/ 4)",
+               "&::mpact::sim::riscv::VlChild";
+
+    // Vector unit-stride segment load
+    vlsege8{(: rs1, vmask, nf:), (: nf : vd)},
+      disasm: "vlseg%nf\\e.v", "%vd, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlSegment, /*element_width*/ 1)",
+               "absl::bind_front(&::mpact::sim::riscv::VlSegmentChild, /*element_width*/ 1)";
+    vlsege16{(: rs1, vmask, nf:), (: nf : vd)},
+      disasm: "vlseg%nf\\e.v", "%vd, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlSegment, /*element_width*/ 2)",
+               "absl::bind_front(&::mpact::sim::riscv::VlSegmentChild, /*element_width*/ 2)";
+    vlsege32{(: rs1, vmask, nf:), (: nf : vd)},
+      disasm: "vlseg%nf\\e.v", "%vd, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlSegment, /*element_width*/ 4)",
+               "absl::bind_front(&::mpact::sim::riscv::VlSegmentChild, /*element_width*/ 4)";
+
+    // Vector strided segment load.
+    vlssege8{(: rs1, rs2, vmask, nf: ), (: nf : vd)},
+      disasm: "vlsseg%nf\\e8.v", "%vd, (%rs1), %rs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlSegmentStrided, /*element_width*/ 1)",
+               "absl::bind_front(&::mpact::sim::riscv::VlSegmentChild, /*element_width*/ 1)";
+    vlssege16{(: rs1, rs2, vmask, nf: ), (: nf : vd)},/*element_width*/ 
+      disasm: "vlsseg%nf\\e16.v", "%vd, (%rs1), %rs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlSegmentStrided, /*element_width*/ 2)",
+               "absl::bind_front(&::mpact::sim::riscv::VlSegmentChild, /*element_width*/ 2)";
+    vlssege32{(: rs1, rs2, vmask, nf: ), (: nf : vd)},
+      disasm: "vlsseg%nf\\e32.v", "%vd, (%rs1), %rs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlSegmentStrided, /*element_width*/ 4)",
+               "absl::bind_front(&::mpact::sim::riscv::VlSegmentChild, /*element_width*/ 4)";
+
+    // Vector indexed segment load unordered.
+    vluxsegei8{(: rs1, vs2, vmask, nf :), (: nf : vd)},
+      disasm: "vluxseg%nf\\ei1.v", "%vd, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlSegmentIndexed, /*index_width*/ 1)",
+               "absl::bind_front(&::mpact::sim::riscv::VlSegmentChild, /*element_width*/ 1)";
+    vluxsegei16{(: rs1, vs2, vmask, nf :), (: nf : vd)},
+      disasm: "vluxseg%nf\\ei2.v", "%vd, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlSegmentIndexed, /*index_width*/ 2)",
+               "absl::bind_front(&::mpact::sim::riscv::VlSegmentChild, /*element_width*/ 2)";
+    vluxsegei32{(: rs1, vs2, vmask, nf :), (: nf : vd)},
+      disasm: "vluxseg%nf\\ei4.v", "%vd, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlSegmentIndexed, /*index_width*/ 4)",
+               "absl::bind_front(&::mpact::sim::riscv::VlSegmentChild, /*element_width*/ 4)";
+
+    // Vector indexed segment load ordered.
+
+    vloxsegei8{(: rs1, vs2, vmask, nf :), (: nf : vd)},
+      disasm: "vluxseg%nf\\ei1.v", "%vd, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlSegmentIndexed, /*index_width*/ 1)",
+               "absl::bind_front(&::mpact::sim::riscv::VlSegmentChild, /*element_width*/ 1)";
+    vloxsegei16{(: rs1, vs2, vmask, nf :), (: nf : vd)},
+      disasm: "vluxseg%nf\\ei2.v", "%vd, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlSegmentIndexed, /*index_width*/ 2)",
+               "absl::bind_front(&::mpact::sim::riscv::VlSegmentChild, /*element_width*/ 2)";
+    vloxsegei32{(: rs1, vs2, vmask, nf :), (: nf : vd)},
+      disasm: "vluxseg%nf\\ei4.v", "%vd, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VlSegmentIndexed, /*index_width*/ 4)",
+               "absl::bind_front(&::mpact::sim::riscv::VlSegmentChild, /*element_width*/ 4)";
+
+    // VECTOR STORES
+
+    // Vector store, unit stride.
+    vse8{: vs3, rs1, const1, vmask : },
+      disasm: "vse8.v", "%vs3, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsStrided, /*element_width*/ 1)";
+    vse16{: vs3, rs1, const2, vmask : },
+      disasm: "vse16.v", "%vs3, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsStrided, /*element_width*/ 2)";
+    vse32{: vs3, rs1, const4, vmask : },
+      disasm: "vse32.v", "%vs3, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsStrided, /*element_width*/ 4)";
+
+    // Vector store mask
+    vsm{: vs3, rs1, const1, vmask_true:},
+      disasm: "vsm",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::Vsm)";
+
+    // Vector store, unit stride, fault first.
+    vse8ff{: vs3, rs1, const1, vmask:},
+      disasm: "vse8ff.v", "%vs3, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsStrided, /*element_width*/ 1)";
+    vse16ff{: vs3, rs1, const2, vmask:},
+      disasm: "vse16ff.v", "%vs3, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsStrided, /*element_width*/ 2)";
+    vse32ff{: vs3, rs1, const4, vmask:},
+      disasm: "vse32ff.v", "%vs3, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsStrided, /*element_width*/ 4)";
+
+    // Vector store register.
+    vs1re8{(: vs3, rs1 :)},
+      disasm: "vs1re8.v", "%vs3, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsRegister, /*num_regs*/ 1)";
+    vs1re16{(: vs3, rs1 :)},
+      disasm: "vs1re16.v", "%vs3, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsRegister, /*num_regs*/ 1)";
+    vs1re32{(: vs3, rs1 :)},
+      disasm: "vs1re32.v", "%vs3, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsRegister, /*num_regs*/ 1)";
+    vs2re8{(: vs3, rs1 :)},
+      disasm: "vs2re8.v", "%vs3, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsRegister, /*num_regs*/ 2)";
+    vs2re16{(: vs3, rs1 :)},
+      disasm: "vs2re16.v", "%vs3, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsRegister, /*num_regs*/ 2)";
+    vs2re32{(: vs3, rs1 :)},
+      disasm: "vs2re32.v", "%vs3, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsRegister, /*num_regs*/ 2)";
+    vs4re8{(: vs3, rs1 :)},
+      disasm: "vs4re8.v", "%vs3, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsRegister, /*num_regs*/ 4)";
+    vs4re16{(: vs3, rs1 :)},
+      disasm: "vs4re16.v", "%vs3, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsRegister, /*num_regs*/ 4)";
+    vs4re32{(: vs3, rs1 :)},
+      disasm: "vs4re32.v", "%vs3, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsRegister, /*num_regs*/ 4)";
+    vs8re8{(: vs3, rs1 :)},
+      disasm: "vs8re8.v", "%vs3, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsRegister, /*num_regs*/8)";
+    vs8re16{(: vs3, rs1 :)},
+      disasm: "vs8re16.v", "%vs3, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsRegister, /*num_regs*/8)";
+    vs8re32{(: vs3, rs1 :)},
+      disasm: "vs8re32.v", "%vs3, (%rs1)",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsRegister, /*num_regs*/8)";
+
+    // Vector store, strided.
+    vsse8{: vs3, rs1, rs2, vmask : },
+      disasm: "vsse8.v", "%vs3, (%rs1), %rs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsStrided, /*element_width*/ 1)";
+    vsse16{: vs3, rs1, rs2, vmask : },
+      disasm: "vsse16.v", "%vs3, (%rs1), %rs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsStrided, /*element_width*/ 2)";
+    vsse32{: vs3, rs1, rs2, vmask : },
+      disasm: "vsse32.v", "%vs3, (%rs1), %rs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsStrided, /*element_width*/ 4)";
+
+    // Vector store, indexed, unordered.
+    vsuxei8{: vs3, rs1, vs2, vmask: },
+      disasm: "vsuxei8", "%vs3, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsIndexed, /*index_width*/ 1)";
+    vsuxei16{: vs3, rs1, vs2, vmask:},
+      disasm: "vsuxei16", "%vs3, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsIndexed, /*index_width*/ 2)";
+    vsuxei32{: vs3, rs1, vs2, vmask:},
+      disasm: "vsuxei32", "%vs3, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsIndexed, /*index_width*/ 4)";
+
+    // Vector store, indexed, unordered
+    vsoxei8{: vs3, rs1, vs2, vmask:},
+      disasm: "vsoxei8", "%vs3, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsIndexed, /*index_width*/ 1)";
+    vsoxei16{: vs3, rs1, vs2, vmask:},
+      disasm: "vsoxei16", "%vs3, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsIndexed, /*index_width*/ 2)";
+    vsoxei32{: vs3, rs1, vs2, vmask:},
+      disasm: "vsoxei32", "%vs3, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsIndexed, /*index_width*/ 4)";
+
+    // Vector unit-stride segment store.
+    vssege8{(: vs3, rs1, vmask, nf:)},
+      disasm: "vsseg%nf\\e.v", "%vs3, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsSegment, /*element_width*/ 1)";
+    vssege16{(: vs3, rs1, vmask, nf:)},
+      disasm: "vsseg%nf\\e.v", "%vs3, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsSegment, /*element_width*/ 2)";
+    vssege32{(: vs3, rs1, vmask, nf:)},
+      disasm: "vsseg%nf\\e.v", "%vs3, (%rs1), %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsSegment, /*element_width*/ 4)";
+
+    // Vector strided segment store.
+    vsssege8{(: vs3, rs1, rs2, vmask, nf: )},
+      disasm: "vssseg%nf\\e8.v", "%vs3, (%rs1), %rs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsSegmentStrided, /*element_width*/ 1)";
+    vsssege16{(: vs3, rs1, rs2, vmask, nf: )},
+      disasm: "vssseg%nf\\e16.v", "%vs3, (%rs1), %rs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsSegmentStrided, /*element_width*/ 2)";
+    vsssege32{(: vs3, rs1, rs2, vmask, nf: )},
+      disasm: "vssseg%nf\\e32.v", "%vs3, (%rs1), %rs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsSegmentStrided, /*element_width*/ 4)";
+
+    // Vector indexed segment store unordered.
+    vsuxsegei8{(: vs3, rs1, vs2, vmask, nf :)},
+      disasm: "vsuxseg%nf\\ei1.v", "%vs3, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsSegmentStrided, /*element_width*/ 1)";
+    vsuxsegei16{(: vs3, rs1, vs2, vmask, nf :)},
+      disasm: "vsuxseg%nf\\ei2.v", "%vs3, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsSegmentStrided, /*element_width*/ 2)";
+    vsuxsegei32{(: vs3, rs1, vs2, vmask, nf :)},
+      disasm: "vsuxseg%nf\\ei4.v", "%vs3, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsSegmentStrided, /*element_width*/ 4)";
+
+    // Vector indexed segment store ordered.
+    vsoxsegei8{(: vs3, rs1, vs2, vmask, nf :)},
+      disasm: "vsuxseg%nf\\ei1.v", "%vs3, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsSegmentIndexed, /*index_width*/ 1)";
+    vsoxsegei16{(: vs3, rs1, vs2, vmask, nf :)},
+      disasm: "vsuxseg%nf\\ei2.v", "%vs3, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsSegmentIndexed, /*index_width*/ 2)";
+    vsoxsegei32{(: vs3, rs1, vs2, vmask, nf :)},
+      disasm: "vsuxseg%nf\\ei4.v", "%vs3, (%rs1), %vs2, %vmask",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::VsSegmentIndexed, /*index_width*/ 4)";
+
+    // Integer OPIVV, OPIVX, OPIVI.
+    vadd_vv{: vs2, vs1, vmask : vd},
+      disasm: "vadd.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vadd";
+    vadd_vx{: vs2, rs1, vmask : vd},
+      disasm: "vadd.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vadd";
+    vadd_vi{: vs2, simm5, vmask : vd},
+      disasm: "vadd.vi", "%vd, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vadd";
+    vsub_vv{: vs2, vs1, vmask : vd},
+      disasm: "vsub.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsub";
+    vsub_vx{: vs2, rs1, vmask : vd},
+      disasm: "vsub.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsub";
+    vrsub_vx{: vs2, rs1, vmask : vd},
+      disasm: "vrsub.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vrsub";
+    vrsub_vi{: vs2, simm5, vmask, vd},
+      disasm: "vrsub.vi", "%vd, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vrsub";
+    vminu_vv{: vs2, vs1, vmask : vd},
+      disasm: "vminu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vminu";
+    vminu_vx{: vs2, rs1, vmask : vd},
+      disasm: "vminu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vminu";
+    vmin_vv{: vs2, vs1, vmask : vd},
+      disasm: "vmin.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmin";
+    vmin_vx{: vs2, rs1, vmask : vd},
+      disasm: "vmin.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmin";
+    vmaxu_vv{: vs2, vs1, vmask : vd},
+      disasm: "vmax.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmaxu";
+    vmaxu_vx{: vs2, rs1, vmask : vd},
+      disasm: "vmax.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmaxu";
+    vmax_vv{: vs2, vs1, vmask : vd},
+      disasm: "vmax.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmax";
+    vmax_vx{: vs2, rs1, vmask : vd},
+      disasm: "vmax.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmax";
+    vand_vv{: vs2, vs1, vmask : vd},
+      disasm: "vand.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vand";
+    vand_vx{: vs2, rs1, vmask : vd},
+      disasm: "vand.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vand";
+    vand_vi{: vs2, simm5, vmask : vd},
+      disasm: "vand.vi", "%vd, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vand";
+    vor_vv{: vs2, vs1, vmask : vd},
+      disasm: "vor.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vor";
+    vor_vx{: vs2, rs1, vmask : vd},
+      disasm: "vor.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vor";
+    vor_vi{: vs2, simm5, vmask : vd},
+      disasm: "vor.vi", "%vd, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vor";
+    vxor_vv{: vs2, vs1, vmask : vd},
+      disasm: "vxor.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vxor";
+    vxor_vx{: vs2, rs1, vmask : vd},
+      disasm: "vxor.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vxor";
+    vxor_vi{: vs2, simm5, vmask : vd},
+      disasm: "vxor.vi", "%vd, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vxor";
+    vrgather_vv{: vs2, vs1, vmask: vd},
+      disasm: "vrgather.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vrgather";
+    vrgather_vx{: vs2, rs1, vmask: vd},
+      disasm: "vrgather.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vrgather";
+    vrgather_vi{: vs2, uimm5, vmask: vd},
+      disasm: "vrgather.vi", "%vd, %uimm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vrgather";
+    vrgatherei16_vv{: vs2, vs1, vmask: vd},
+      disasm: "vrgatherei16.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vrgatherei16";
+    vslideup_vx{: vs2, rs1, vmask: vd},
+      disasm: "vslideup.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vslideup";
+    vslideup_vi{: vs2, uimm5, vmask: vd},
+      disasm: "vslideup.vi", "%vd, %vs2, %uimm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vslideup";
+    vslidedown_vx{: vs2, rs1, vmask: vd},
+      disasm: "vslidedown.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vslidedown";
+    vslidedown_vi{: vs2, uimm5, vmask: vd},
+      disasm: "vslidedown.vi", "%vd, %vs2, %uimm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vslidedown";
+    vadc_vv{: vs2, vs1, vmask: vd},
+      disasm: "vadc.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vadc";
+    vadc_vx{: vs2, rs1, vmask: vd},
+      disasm: "vadc.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vadc";
+    vadc_vi{: vs2, simm5, vmask: vd},
+      disasm: "vadc.vi", "%vd, %vs2, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vadc";
+    vmadc_vv{: vs2, vs1, vmask, vm: vd},
+      disasm: "vmadc.vv", "%vd, %vs2, %vs1, %vmask, %vm ",
+      semfunc: "&::mpact::sim::riscv::Vmadc";
+    vmadc_vx{: vs2, rs1, vmask, vm: vd},
+      disasm: "vmadc.vx", "%vd, %vs2, %rs1, %vmask, %vm",
+      semfunc: "&::mpact::sim::riscv::Vmadc";
+    vmadc_vi{: vs2, simm5, vmask, vm: vd},
+      disasm: "vmadc.vi", "%vd, %vs2, %simm5, %vmask, %vm",
+      semfunc: "&::mpact::sim::riscv::Vmadc";
+    vsbc_vv{: vs2, vs1, vmask: vd},
+      disasm: "vsbc.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsbc";
+    vsbc_vx{: vs2, rs1, vmask: vd},
+      disasm: "vsbc.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsbc";
+    vmsbc_vv{: vs2, vs1, vmask, vm: vd},
+      disasm: "vmsbc.vv", "%vd, %vs2, %vs1, %vmask, %vm",
+      semfunc: "&::mpact::sim::riscv::Vmsbc";
+    vmsbc_vx{: vs2, rs1, vmask, vm: vd},
+      disasm: "vmsbc.vx", "%vd, %vs2, %rs1, %vmask, %vm",
+      semfunc: "&::mpact::sim::riscv::Vmsbc";
+    vmerge_vv{: vs2, vs1, vmask: vd},
+      disasm: "vmerge.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmerge";
+    vmerge_vx{: vs2, rs1, vmask: vd},
+      disasm: "vmerge.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmerge";
+    vmerge_vi{: vs2, simm5, vmask: vd},
+      disasm: "vmerge.vi", "%vd, %vs2, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmerge";
+    vmv_vv{: vs2, vs1, vmask_true: vd},
+      disasm: "vmv.vv", "%vd, %vs1",
+      semfunc: "&::mpact::sim::riscv::Vmerge";
+    vmv_vx{: vs2, rs1, vmask_true: vd},
+      disasm: "vmv.vx", "%vd, %rs1",
+      semfunc: "&::mpact::sim::riscv::Vmerge";
+    vmv_vi{: vs2, simm5, vmask_true: vd},
+      disasm: "vmv.vi", "%vd, %simm5",
+      semfunc: "&::mpact::sim::riscv::Vmerge";
+    vmseq_vv{: vs2, vs1, vmask: vd},
+      disasm: "vmseq.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmseq";
+    vmseq_vx{: vs2, rs1, vmask: vd},
+      disasm: "vmseq.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmseq";
+    vmseq_vi{: vs2, simm5, vmask: vd},
+      disasm: "vmseq.vi", "%vd, %vs2, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmseq";
+    vmsne_vv{: vs2, vs1, vmask: vd},
+      disasm: "vmsne.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsne";
+    vmsne_vx{: vs2, rs1, vmask: vd},
+      disasm: "vmsne.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsne";
+    vmsne_vi{: vs2, simm5, vmask: vd},
+      disasm: "vmsne.vi", "%vd, %vs2, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsne";
+    vmsltu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vmsltu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsltu";
+    vmsltu_vx{: vs2, rs1, vmask: vd},
+      disasm: "vmsltu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsltu";
+    vmslt_vv{: vs2, vs1, vmask: vd},
+      disasm: "vmslt.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmslt";
+    vmslt_vx{: vs2, rs1, vmask: vd},
+      disasm: "vmslt.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmslt";
+    vmsleu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vmsleu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsleu";
+    vmsleu_vx{: vs2, rs1, vmask: vd},
+      disasm: "vmsleu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsleu";
+    vmsleu_vi{: vs2, simm5, vmask: vd},
+      disasm: "vmsleu.vi", "%vd, %vs2, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsleu";
+    vmsle_vv{: vs2, vs1, vmask: vd},
+      disasm: "vmsle.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsle";
+    vmsle_vx{: vs2, rs1, vmask: vd},
+      disasm: "vmsle.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsle";
+    vmsle_vi{: vs2, simm5, vmask: vd},
+      disasm: "vmsle.vi", "%vd, %vs2, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsle";
+    vmsgtu_vx{: vs2, rs1, vmask: vd},
+      disasm: "vmsgtu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsgtu";
+    vmsgtu_vi{: vs2, simm5, vmask: vd},
+      disasm: "vmsgtu.vi", "%vd, %vs2, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsgtu";
+    vmsgt_vx{: vs2, rs1, vmask: vd},
+      disasm: "vmsgt.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsgt";
+    vmsgt_vi{: vs2, simm5, vmask: vd},
+      disasm: "vmsgt.vi", "%vd, %vs2, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsgt";
+    vsaddu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vsaddu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsaddu";
+    vsaddu_vx{: vs2, rs1, vmask: vd},
+      disasm: "vsaddu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsaddu";
+    vsaddu_vi{: vs2, simm5, vmask: vd},
+      disasm: "vsaddu.vi", "%vd, %vs2, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsaddu";
+    vsadd_vv{: vs2, vs1, vmask: vd},
+      disasm: "vsadd.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsadd";
+    vsadd_vx{: vs2, rs1, vmask: vd},
+      disasm: "vsadd.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsadd";
+    vsadd_vi{: vs2, simm5, vmask: vd},
+      disasm: "vsadd.vi", "%vd, %vs2, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsadd";
+    vssubu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vssubu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vssubu";
+    vssubu_vx{: vs2, rs1, vmask: vd},
+      disasm: "vssubu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vssubu";
+    vssub_vv{: vs2, vs1, vmask: vd},
+      disasm: "vssub.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vssub";
+    vssub_vx{: vs2, rs1, vmask: vd},
+      disasm: "vssub.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vssub";
+    vsll_vv{: vs2, vs1, vmask : vd},
+      disasm: "vsll.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsll";
+    vsll_vx{: vs2, rs1, vmask : vd},
+      disasm: "vsll.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsll";
+    vsll_vi{: vs2, simm5, vmask: vd},
+      disasm: "vsll.vi", "%vd, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsll";
+    vsmul_vv{: vs2, vs1, vmask : vd},
+      disasm: "vsmul.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsmul";
+    vsmul_vx{: vs2, rs1, vmask : vd},
+      disasm: "vsmul.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsmul";
+    vmv1r_vi{: vs2 : vd},
+      disasm: "vmv1r.vi", "%vd, %vs2",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::Vmvr, 1)";
+    vmv2r_vi{: vs2 : vd},
+      disasm: "vmv2r.vi", "%vd, %vs2",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::Vmvr, 2)";
+    vmv4r_vi{: vs2 : vd},
+      disasm: "vmv4r.vi", "%vd, %vs2",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::Vmvr, 4)";
+    vmv8r_vi{: vs2 : vd},
+      disasm: "vmv8r.vi", "%vd, %vs2",
+      semfunc: "absl::bind_front(&::mpact::sim::riscv::Vmvr, 8)";
+    vsrl_vv{: vs2, vs1, vmask : vd},
+      disasm: "vsrl.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsrl";
+    vsrl_vx{: vs2, rs1, vmask : vd},
+      disasm: "vsrl.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsrl";
+    vsrl_vi{: vs2, simm5, vmask: vd},
+      disasm: "vsrl.vi", "%vd, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsrl";
+    vsra_vv{: vs2, vs1, vmask : vd},
+      disasm: "vsra.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsra";
+    vsra_vx{: vs2, rs1, vmask : vd},
+      disasm: "vsra.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsra";
+    vsra_vi{: vs2, simm5, vmask: vd},
+      disasm: "vsra.vi", "%vd, %simm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsra";
+    vssrl_vv{: vs2, vs1, vmask: vd},
+      disasm: "vssrl.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vssrl";
+    vssrl_vx{: vs2, rs1, vmask: vd},
+      disasm: "vssrl.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vssrl";
+    vssrl_vi{: vs2, uimm5, vmask: vd},
+      disasm: "vssrl.vi", "%vd, %vs2, %uimm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vssrl";
+    vssra_vv{: vs2, vs1, vmask: vd},
+      disasm: "vssra.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vssra";
+    vssra_vx{: vs2, rs1, vmask: vd},
+      disasm: "vssra.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vssra";
+    vssra_vi{: vs2, uimm5, vmask: vd},
+      disasm: "vssra.vi", "%vd, %vs2, %uimm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vssra";
+    vnsrl_vv{: vs2, vs1, vmask : vd},
+      disasm: "vnsrl.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnsrl";
+    vnsrl_vx{: vs2, rs1, vmask : vd},
+      disasm: "vnsrl.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnsrl";
+    vnsrl_vi{: vs2, uimm5, vmask : vd},
+      disasm: "vnsrl.vi", "%vd, %vs2, %uimm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnsrl";
+    vnsra_vv{: vs2, vs1, vmask : vd},
+      disasm: "vnsra.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnsra";
+    vnsra_vx{: vs2, rs1, vmask : vd},
+      disasm: "vnsra.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnsra";
+    vnsra_vi{: vs2, uimm5, vmask : vd},
+      disasm: "vnsra.vi", "%vd, %vs2, %uimm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnsra";
+    vnclipu_vv{: vs2, vs1, vmask : vd},
+      disasm: "vnclipu_vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnclipu";
+    vnclipu_vx{: vs2, rs1, vmask : vd},
+      disasm: "vnclipu_vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnclipu";
+    vnclipu_vi{: vs2, uimm5, vmask : vd},
+      disasm: "vnclipu_vi", "%vd, %vs2, %uimm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnclipu";
+    vnclip_vv{: vs2, vs1, vmask : vd},
+      disasm: "vnclip_vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnclip";
+    vnclip_vx{: vs2, rs1, vmask : vd},
+      disasm: "vnclip_vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnclip";
+    vnclip_vi{: vs2, uimm5, vmask : vd},
+      disasm: "vnclip_vi", "%vd, %vs2, %uimm5, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnclip";
+    vwredsumu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vwredsumu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwredsumu";
+    vwredsum_vv{: vs2, vs1, vmask: vd},
+      disasm: "vwredsum.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwredsum";
+
+    // Integer OPMVV, OPMVX.
+    vredsum_vv{: vs2, vs1, vmask: vd},
+      disasm: "vredsum.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vredsum";
+    vredand_vv{: vs2, vs1, vmask: vd},
+      disasm: "vredand.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vredand";
+    vredor_vv{: vs2, vs1, vmask: vd},
+      disasm: "vredor.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vredor";
+    vredxor_vv{: vs2, vs1, vmask: vd},
+      disasm: "vredxor.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vredxor";
+    vredminu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vredminu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vredminu";
+    vredmin_vv{: vs2, vs1, vmask: vd},
+      disasm: "vredmin.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vredmin";
+    vredmaxu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vredmaxu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vredmaxu";
+    vredmax_vv{: vs2, vs1, vmask: vd},
+      disasm: "vredmax.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vredmax";
+    vaaddu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vaaddu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vaaddu";
+    vaaddu_vx{: vs2, rs1, vmask: vd},
+      disasm: "vaaddu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vaaddu";
+    vaadd_vv{: vs2, vs1, vmask: vd},
+      disasm: "vaadd.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vaadd";
+    vaadd_vx{: vs2, rs1, vmask: vd},
+      disasm: "vaadd.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vaadd";
+    vasubu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vasubu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vasubu";
+    vasubu_vx{: vs2, rs1, vmask: vd},
+      disasm: "vasubu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vasubu";
+    vasub_vv{: vs2, vs1, vmask: vd},
+      disasm: "vasub.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vasub";
+    vasub_vx{: vs2, rs1, vmask: vd},
+      disasm: "vasub.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vasub";
+    vslide1up_vx{: vs2, rs1, vmask: vd},
+      disasm: "vslide1up.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vslide1up";
+    vslide1down_vx{: vs2, rs1, vmask: vd},
+      disasm: "vslide1down.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vslide1down";
+    vcompress_vv{: vs2, vs1: vd},
+      disasm: "vcompress.vv", "%vd, %vs2, %vs1",
+      semfunc: "&::mpact::sim::riscv::Vcompress";
+    vmandnot_vv{: vs2, vs1: vd},
+      disasm: "vwmandnot.vv", "%vd, %vs2, %vs1",
+      semfunc: "&::mpact::sim::riscv::Vmandnot";
+    vmand_vv{: vs2, vs1: vd},
+      disasm: "vmand.vv", "%vd, %vs2, %vs1",
+      semfunc: "&::mpact::sim::riscv::Vmand";
+    vmor_vv{: vs2, vs1: vd},
+      disasm: "vmor.vv", "%vd, %vs2, %vs1",
+      semfunc: "&::mpact::sim::riscv::Vmor";
+    vmxor_vv{: vs2, vs1: vd},
+      disasm: "vmxor.vv", "%vd, %vs2, %vs1",
+      semfunc: "&::mpact::sim::riscv::Vmxor";
+    vmornot_vv{: vs2, vs1: vd},
+      disasm: "vmornot.vv", "%vd, %vs2, %vs1",
+      semfunc: "&::mpact::sim::riscv::Vmornot";
+    vmnand_vv{: vs2, vs1: vd},
+      disasm: "vmnand.vv", "%vd, %vs2, %vs1",
+      semfunc: "&::mpact::sim::riscv::Vmnand";
+    vmnor_vv{: vs2, vs1: vd},
+      disasm: "vmnor.vv", "%vd, %vs2, %vs1",
+      semfunc: "&::mpact::sim::riscv::Vmnor";
+    vmxnor_vv{: vs2, vs1: vd},
+      disasm: "vmxnor.vv", "%vd, %vs2, %vs1",
+      semfunc: "&::mpact::sim::riscv::Vmxnor";
+    vdivu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vdivu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vdivu";
+    vdivu_vx{: vs2, rs1, vmask: vd},
+      disasm: "vdivu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vdivu";
+    vdiv_vv{: vs2, vs1, vmask: vd},
+      disasm: "vdiv.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vdiv";
+    vdiv_vx{: vs2, rs1, vmask: vd},
+      disasm: "vdiv.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vdiv";
+    vremu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vremu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vremu";
+    vremu_vx{: vs2, rs1, vmask: vd},
+      disasm: "vremu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vremu";
+    vrem_vv{: vs2, vs1, vmask: vd},
+      disasm: "vrem.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vrem";
+    vrem_vx{: vs2, rs1, vmask: vd},
+      disasm: "vrem.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vrem";
+    vmulhu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vmulhu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmulhu";
+    vmulhu_vx{: vs2, rs1, vmask: vd},
+      disasm: "vmulhu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmulhu";
+    vmul_vv{: vs2, vs1, vmask: vd},
+      disasm: "vmul.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmul";
+    vmul_vx{: vs2, rs1, vmask: vd},
+      disasm: "vmul.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmul";
+    vmulhsu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vmulhsu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmulhsu";
+    vmulhsu_vx{: vs2, rs1, vmask: vd},
+      disasm: "vmulhsu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmulhsu";
+    vmulh_vv{: vs2, vs1, vmask: vd},
+      disasm: "vmulh.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmulh";
+    vmulh_vx{: vs2, rs1, vmask: vd},
+      disasm: "vmulh.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmulh";
+    vmadd_vv{: vs2, vs1, vd, vmask: vd},
+      disasm: "vmadd.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmadd";
+    vmadd_vx{: vs2, rs1, vd, vmask: vd},
+      disasm: "vmadd.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmadd";
+    vnmsub_vv{: vs2, vs1, vd, vmask: vd},
+      disasm: "vnmsub.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnmsub";
+    vnmsub_vx{: vs2, rs1, vd, vmask: vd},
+      disasm: "vnmsub.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnmsub";
+    vmacc_vv{: vs2, vs1, vd, vmask: vd},
+      disasm: "vmacc.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmacc";
+    vmacc_vx{: vs2, rs1, vd, vmask: vd},
+      disasm: "vmacc.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmacc";
+    vnmsac_vv{: vs2, vs1, vd, vmask: vd},
+      disasm: "vnmsac.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnmsac";
+    vnmsac_vx{: vs2, rs1, vd, vmask: vd},
+      disasm: "vnmsac.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vnmsac";
+    vwaddu_vv{: vs2, vs1, vmask : vd},
+      disasm: "vwaddu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwaddu";
+    vwaddu_vx{: vs2, rs1, vmask : vd},
+      disasm: "vwaddu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwaddu";
+    vwadd_vv{: vs2, vs1, vmask : vd},
+      disasm: "vwadd_vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwadd";
+    vwadd_vx{: vs2, rs1, vmask : vd},
+      disasm: "vwadd.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwadd";
+    vwsubu_vv{: vs2, vs1, vmask : vd},
+      disasm: "vwsubu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwsubu";
+    vwsubu_vx{: vs2, rs1, vmask : vd},
+      disasm: "vwsubu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwsubu";
+    vwsub_vv{: vs2, vs1, vmask : vd},
+      disasm: "vwsub.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwsub";
+    vwsub_vx{: vs2, rs1, vmask : vd},
+      disasm: "vwsub.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwsub";
+    vwaddu_w_vv{: vs2, vs1, vmask : vd},
+      disasm: "vwaddu.wv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwadduw";
+    vwaddu_w_vx{: vs2, rs1, vmask : vd},
+      disasm: "vwaddu.wx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwadduw";
+    vwadd_w_vv{: vs2, vs1, vmask : vd},
+      disasm: "vwadd.wv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwaddw";
+    vwadd_w_vx{: vs2, rs1, vmask : vd},
+      disasm: "vwadd.wx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwaddw";
+    vwsubu_w_vv{: vs2, vs1, vmask : vd},
+      disasm: "vwsubu.wv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwsubuw";
+    vwsubu_w_vx{: vs2, rs1, vmask : vd},
+      disasm: "vwsubu.wx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwsubuw";
+    vwsub_w_vv{: vs2, vs1, vmask : vd},
+      disasm: "vwsub.wv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwsubw";
+    vwsub_w_vx{: vs2, rs1, vmask : vd},
+      disasm: "vwsub.wx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwsubw";
+    vwmulu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vwmulu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwmulu";
+    vwmulu_vx{: vs2, rs1, vmask: vd},
+      disasm: "vwmulu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwmulu";
+    vwmulsu_vv{: vs2, vs1, vmask: vd},
+      disasm: "vwmulsu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwmulsu";
+    vwmulsu_vx{: vs2, rs1, vmask: vd},
+      disasm: "vwmulsu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwmulsu";
+    vwmul_vv{: vs2, vs1, vmask: vd},
+      disasm: "vwmul.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwmul";
+    vwmul_vx{: vs2, rs1, vmask: vd},
+      disasm: "vwmul.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwmul";
+    vwmaccu_vv{: vs2, vs1, vd, vmask: vd},
+      disasm: "vwmaccu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwmaccu";
+    vwmaccu_vx{: vs2, rs1, vd, vmask: vd},
+      disasm: "vwmaccu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwmaccu";
+    vwmacc_vv{: vs2, vs1, vd, vmask: vd},
+      disasm: "vwmacc.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwmacc";
+    vwmacc_vx{: vs2, rs1, vd, vmask: vd},
+      disasm: "vwmacc.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwmacc";
+    vwmaccus_vv{: vs2, vs1, vd, vmask: vd},
+      disasm: "vwmaccus.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwmaccus";
+    vwmaccus_vx{: vs2, rs1, vd, vmask: vd},
+      disasm: "vwmaccus.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwmaccus";
+    vwmaccsu_vv{: vs2, vs1, vd, vmask: vd},
+      disasm: "vwmaccsu.vv", "%vd, %vs2, %vs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwmaccsu";
+    vwmaccsu_vx{: vs2, rs1, vd, vmask: vd},
+      disasm: "vwmaccsu.vx", "%vd, %vs2, %rs1, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vwmaccsu";
+
+    // VWXUNARY0
+    vmv_x_s{: vs2 : rd},
+      disasm: "vmv.x.s", "%rd, %vs2",
+      semfunc: "&::mpact::sim::riscv::VmvToScalar";
+    vcpop{: vs2, vmask: rd},
+      disasm: "vcpop", "%rd, %vs2, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vcpop";
+    vfirst{: vs2, vmask: rd},
+      disasm: "vfirst", "%rd, %vs2, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vfirst";
+    // VRXUNARY0
+    vmv_s_x{: rs1 : vd},
+      disasm: "vmv.s.x", "%vd, %rs1",
+      semfunc: "&::mpact::sim::riscv::VmvFromScalar";
+    // VXUNARY0
+    vzext_vf8{: vs2, vmask: vd},
+      disasm: "vzext.vf8", "%vd, %vs2, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vzext8";
+    vsext_vf8{: vs2, vmask: vd},
+      disasm: "vsext.vf8", "%vd, %vs2, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsext8";
+    vzext_vf4{: vs2, vmask: vd},
+      disasm: "vzext.vf4", "%vd, %vs2, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vzext4";
+    vsext_vf4{: vs2, vmask: vd},
+      disasm: "vsext.vf4", "%vd, %vs2, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsext4";
+    vzext_vf2{: vs2, vmask: vd},
+      disasm: "vzext.vf2", "%vd, %vs2, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vzext2";
+    vsext_vf2{: vs2, vmask: vd},
+      disasm: "vsext.vf2", "%vd, %vs2, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vsext2";
+    // VMUNARY0
+    vmsbf{:vs2, vmask: vd},
+      disasm: "vmsbf.m", "%vd, %vs2, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsbf";
+    vmsof{:vs2, vmask: vd},
+      disasm: "vmsof.m", "%vd, %vs2, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsof";
+    vmsif{:vs2, vmask: vd},
+      disasm: "vmsif.m", "%vd, %vs2, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vmsif";
+    viota{:vs2, vmask: vd},
+      disasm: "viota.m", "%vd, %vs2, %vmask",
+      semfunc: "&::mpact::sim::riscv::Viota";
+    vid{: vmask: vd},
+      disasm: "vid.v", "%vd, %vmask",
+      semfunc: "&::mpact::sim::riscv::Vid";
+  }
+}
+
+
+// Combining all kelvin instruction sets.
+slot kelvin_v2 : riscv32i, riscv32_hints, riscv32m, riscv32f, zicsr, zfencei,
+                 privileged, riscv32_zbb, riscv32_zbb_imm, riscv_zve32x {
+  default opcode =
+    disasm: "Illegal instruction at 0x%(@:08x)",
+    semfunc: "&::mpact::sim::riscv::RiscVIUnimplemented";
+  opcodes {
+    mpause{},
+      disasm: "mpause",
+      // mpause is the software breakpoint to terminate the program.
+      // TODO: b/441594698 - Add a real implementation for mpause.
+      semfunc: "&::mpact::sim::riscv::RiscVIUnimplemented";
+  }
+}
diff --git a/sim/kelvin_v2_encoding.cc b/sim/kelvin_v2_encoding.cc
new file mode 100644
index 0000000..a651b7d
--- /dev/null
+++ b/sim/kelvin_v2_encoding.cc
@@ -0,0 +1,68 @@
+// 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 "sim/kelvin_v2_encoding.h"
+
+#include <cstdint>
+
+#include "sim/kelvin_v2_bin_decoder.h"
+#include "sim/kelvin_v2_getters.h"
+#include "sim/kelvin_v2_state.h"
+#include "absl/base/nullability.h"
+#include "riscv/riscv_encoding_common.h"
+#include "mpact/sim/generic/operand_interface.h"
+#include "mpact/sim/generic/type_helpers.h"
+
+namespace kelvin::sim {
+
+using ::kelvin::sim::KelvinV2State;
+using ::mpact::sim::generic::DestinationOperandInterface;
+using ::mpact::sim::generic::operator*;  // NOLINT
+using ::mpact::sim::generic::SourceOperandInterface;
+using Extractors = ::kelvin::sim::encoding::Extractors;
+
+KelvinV2Encoding::KelvinV2Encoding(KelvinV2State* /*absl_nonnull*/ state)
+    : RiscVEncodingCommon(), state_(state) {
+  source_op_getters_.emplace(
+      *SourceOpEnum::kNone,
+      []() -> SourceOperandInterface* { return nullptr; });
+  dest_op_getters_.emplace(
+      *DestOpEnum::kNone,
+      [](int latency) -> DestinationOperandInterface* { return nullptr; });
+  // Add Kelvin V2 ISA source operand getters.
+  AddKelvinV2SourceGetters<SourceOpEnum, Extractors>(source_op_getters_, this);
+  // Add Kelvin V2 ISA destination operand getters.
+  AddKelvinV2DestGetters<DestOpEnum, Extractors>(dest_op_getters_, this);
+}
+
+SourceOperandInterface* KelvinV2Encoding::GetSource(SlotEnum, int, OpcodeEnum,
+                                                    SourceOpEnum op, int) {
+  auto const& iter = source_op_getters_.find(*op);
+  if (iter == source_op_getters_.end()) return nullptr;
+  return iter->second();
+}
+
+DestinationOperandInterface* KelvinV2Encoding::GetDestination(
+    SlotEnum, int, OpcodeEnum, DestOpEnum op, int, int latency) {
+  auto const& iter = dest_op_getters_.find(*op);
+  if (iter == dest_op_getters_.end()) return nullptr;
+  return iter->second(latency);
+}
+
+void KelvinV2Encoding::ParseInstruction(uint32_t inst_word) {
+  inst_word_ = inst_word;
+  opcode_ = ::kelvin::sim::encoding::DecodeKelvinV2Inst32(inst_word_);
+}
+
+}  // namespace kelvin::sim
diff --git a/sim/kelvin_v2_encoding.h b/sim/kelvin_v2_encoding.h
new file mode 100644
index 0000000..a50e7e2
--- /dev/null
+++ b/sim/kelvin_v2_encoding.h
@@ -0,0 +1,97 @@
+// 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 SIM_KELVIN_V2_ENCODING_H_
+#define SIM_KELVIN_V2_ENCODING_H_
+
+#include <cstdint>
+
+#include "sim/kelvin_v2_decoder.h"
+#include "sim/kelvin_v2_enums.h"
+#include "sim/kelvin_v2_state.h"
+#include "absl/base/nullability.h"
+#include "absl/container/flat_hash_map.h"
+#include "absl/functional/any_invocable.h"
+#include "riscv/riscv_encoding_common.h"
+#include "mpact/sim/generic/operand_interface.h"
+#include "mpact/sim/generic/simple_resource.h"
+
+namespace kelvin::sim {
+
+class KelvinV2Encoding : public ::kelvin::sim::isa32_v2::KelvinV2EncodingBase,
+                         public ::mpact::sim::riscv::RiscVEncodingCommon {
+ public:
+  using DestinationOperandInterface =
+      ::mpact::sim::generic::DestinationOperandInterface;
+  using SimpleResourcePool = ::mpact::sim::generic::SimpleResourcePool;
+  using SourceOperandInterface = ::mpact::sim::generic::SourceOperandInterface;
+  using KelvinV2State = ::kelvin::sim::KelvinV2State;
+
+  using SourceOpGetterMap =
+      absl::flat_hash_map<int, absl::AnyInvocable<SourceOperandInterface*()>>;
+  using DestOpGetterMap = absl::flat_hash_map<
+      int, absl::AnyInvocable<DestinationOperandInterface*(int)>>;
+
+  using OpcodeEnum = ::kelvin::sim::isa32_v2::OpcodeEnum;
+  using SlotEnum = ::kelvin::sim::isa32_v2::SlotEnum;
+  using SourceOpEnum = ::kelvin::sim::isa32_v2::SourceOpEnum;
+  using DestOpEnum = ::kelvin::sim::isa32_v2::DestOpEnum;
+
+  explicit KelvinV2Encoding(KelvinV2State* /*absl_nonnull*/ state);
+
+  // Based on KelvinV2EncodingBase
+  OpcodeEnum GetOpcode(SlotEnum, int) override { return opcode_; }
+
+  // The following method returns a source operand that corresponds to the
+  // particular operand field.
+  SourceOperandInterface* GetSource(SlotEnum, int, OpcodeEnum, SourceOpEnum op,
+                                    int source_no) override;
+
+  // The following method returns a destination operand that corresponds to the
+  // particular operand field.
+  DestinationOperandInterface* GetDestination(SlotEnum, int, OpcodeEnum,
+                                              DestOpEnum op, int dest_no,
+                                              int latency) override;
+
+  // This method returns latency for any destination operand for which the
+  // latency specifier in the .isa file is '*'. Since there are none, just
+  // return 0.
+  int GetLatency(SlotEnum, int, OpcodeEnum, DestOpEnum, int) override {
+    return 0;
+  }
+
+  // Based on RiscVEncodingCommon
+  KelvinV2State* state() const override { return state_; }
+
+  SimpleResourcePool* resource_pool() override { return nullptr; }
+
+  uint32_t inst_word() const override { return inst_word_; }
+
+  // Parses an instruction and determines the opcode.
+  void ParseInstruction(uint32_t inst_word);
+
+  const SourceOpGetterMap& source_op_getters() { return source_op_getters_; }
+  const DestOpGetterMap& dest_op_getters() { return dest_op_getters_; }
+
+ private:
+  uint32_t inst_word_;
+  OpcodeEnum opcode_;
+  KelvinV2State* state_;
+  SourceOpGetterMap source_op_getters_;
+  DestOpGetterMap dest_op_getters_;
+};
+
+}  // namespace kelvin::sim
+
+#endif  // SIM_KELVIN_V2_ENCODING_H_
diff --git a/sim/kelvin_v2_getters.h b/sim/kelvin_v2_getters.h
new file mode 100644
index 0000000..e970f7a
--- /dev/null
+++ b/sim/kelvin_v2_getters.h
@@ -0,0 +1,318 @@
+// 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 SIM_KELVIN_V2_GETTERS_H_
+#define SIM_KELVIN_V2_GETTERS_H_
+
+#include <cstdint>
+
+#include "absl/base/nullability.h"
+#include "absl/container/flat_hash_map.h"
+#include "absl/functional/any_invocable.h"
+#include "absl/status/statusor.h"
+#include "absl/strings/str_cat.h"
+#include "riscv/riscv_csr.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"
+#include "riscv/riscv_state.h"
+#include "mpact/sim/generic/immediate_operand.h"
+#include "mpact/sim/generic/literal_operand.h"
+#include "mpact/sim/generic/operand_interface.h"
+#include "mpact/sim/generic/type_helpers.h"
+
+namespace kelvin::sim {
+using ::mpact::sim::generic::DestinationOperandInterface;
+using ::mpact::sim::generic::ImmediateOperand;
+using ::mpact::sim::generic::IntLiteralOperand;
+using ::mpact::sim::generic::operator*;  // NOLINT
+using ::mpact::sim::generic::SourceOperandInterface;
+using ::mpact::sim::riscv::GetVectorRegisterSourceOp;
+using ::mpact::sim::riscv::Insert;
+using ::mpact::sim::riscv::kFRegisterAliases;
+using ::mpact::sim::riscv::kXRegisterAliases;
+using ::mpact::sim::riscv::RiscVCsrInterface;
+using ::mpact::sim::riscv::RiscVEncodingCommon;
+using ::mpact::sim::riscv::RiscVState;
+using ::mpact::sim::riscv::RV32Register;
+using ::mpact::sim::riscv::RV32VectorTrueOperand;
+using ::mpact::sim::riscv::RVFpRegister;
+using ::mpact::sim::riscv::RVVectorRegister;
+
+using SourceOpGetterMap =
+    absl::flat_hash_map<int, absl::AnyInvocable<SourceOperandInterface*()>>;
+using DestOpGetterMap =
+    absl::flat_hash_map<int,
+                        absl::AnyInvocable<DestinationOperandInterface*(int)>>;
+
+template <typename SourceOpEnum, typename Extractors>
+void AddKelvinV2SourceGetters(SourceOpGetterMap& getter_map,
+                              RiscVEncodingCommon* /*absl_nonnull*/ common) {
+  // Source operand getters.
+  Insert(getter_map, *SourceOpEnum::kBImm12,
+         [common]() -> SourceOperandInterface* {
+           return new ImmediateOperand<int32_t>(
+               Extractors::Inst32Format::ExtractBImm(common->inst_word()));
+         });
+  Insert(getter_map, *SourceOpEnum::kCSRUimm5,
+         [common]() -> SourceOperandInterface* {
+           return new ImmediateOperand<uint32_t>(
+               Extractors::Inst32Format::ExtractIUimm5(common->inst_word()));
+         });
+  Insert(getter_map, *SourceOpEnum::kConst1, []() -> SourceOperandInterface* {
+    return new ImmediateOperand<uint32_t>(1);
+  });
+  Insert(getter_map, *SourceOpEnum::kConst2, []() -> SourceOperandInterface* {
+    return new ImmediateOperand<uint32_t>(2);
+  });
+  Insert(getter_map, *SourceOpEnum::kConst4, []() -> SourceOperandInterface* {
+    return new ImmediateOperand<uint32_t>(4);
+  });
+  Insert(getter_map, *SourceOpEnum::kCsr,
+         [common]() -> SourceOperandInterface* {
+           uint16_t csr_index =
+               Extractors::Inst32Format::ExtractUImm12(common->inst_word());
+           absl::StatusOr<RiscVCsrInterface*> csr_status =
+               common->state()->csr_set()->GetCsr(csr_index);
+           if (!csr_status.ok()) {
+             return new ImmediateOperand<uint32_t>(csr_index);
+           } else {
+             return new ImmediateOperand<uint32_t>(csr_index,
+                                                   (*csr_status)->name());
+           }
+         });
+  Insert(getter_map, *SourceOpEnum::kFrs1,
+         [common]() -> SourceOperandInterface* {
+           int num = Extractors::RType::ExtractRs1(common->inst_word());
+           return GetRegisterSourceOp<RVFpRegister>(
+               common->state(), absl::StrCat(RiscVState::kFregPrefix, num),
+               kFRegisterAliases[num]);
+         });
+  Insert(getter_map, *SourceOpEnum::kFrs2,
+         [common]() -> SourceOperandInterface* {
+           int num = Extractors::RType::ExtractRs2(common->inst_word());
+           return GetRegisterSourceOp<RVFpRegister>(
+               common->state(), absl::StrCat(RiscVState::kFregPrefix, num),
+               kFRegisterAliases[num]);
+         });
+  Insert(getter_map, *SourceOpEnum::kFrs3,
+         [common]() -> SourceOperandInterface* {
+           int num = Extractors::R4Type::ExtractRs3(common->inst_word());
+           return GetRegisterSourceOp<RVFpRegister>(
+               common->state(), absl::StrCat(RiscVState::kFregPrefix, num),
+               kFRegisterAliases[num]);
+         });
+  Insert(getter_map, *SourceOpEnum::kIImm12,
+         [common]() -> SourceOperandInterface* {
+           return new ImmediateOperand<int32_t>(
+               Extractors::IType::ExtractImm12(common->inst_word()));
+         });
+  Insert(getter_map, *SourceOpEnum::kIUimm5,
+         [common]() -> SourceOperandInterface* {
+           return new ImmediateOperand<uint32_t>(
+               Extractors::IType::ExtractIUimm5(common->inst_word()));
+         });
+  Insert(getter_map, *SourceOpEnum::kJImm12,
+         [common]() -> SourceOperandInterface* {
+           return new ImmediateOperand<int32_t>(
+               Extractors::Inst32Format::ExtractImm12(common->inst_word()));
+         });
+  Insert(getter_map, *SourceOpEnum::kJImm20,
+         [common]() -> SourceOperandInterface* {
+           return new ImmediateOperand<int32_t>(
+               Extractors::JType::ExtractJImm(common->inst_word()));
+         });
+  Insert(getter_map, *SourceOpEnum::kNf, [common]() -> SourceOperandInterface* {
+    int num_fields = Extractors::VMem::ExtractNf(common->inst_word());
+    return new ImmediateOperand<uint8_t>(num_fields,
+                                         absl::StrCat(num_fields + 1));
+  });
+  Insert(getter_map, *SourceOpEnum::kPred,
+         [common]() -> SourceOperandInterface* {
+           return new ImmediateOperand<uint32_t>(
+               Extractors::Fence::ExtractPred(common->inst_word()));
+         });
+  Insert(getter_map, *SourceOpEnum::kRUimm5,
+         [common]() -> SourceOperandInterface* {
+           return new ImmediateOperand<uint32_t>(
+               Extractors::RType::ExtractRUimm5(common->inst_word()));
+         });
+  Insert(getter_map, *SourceOpEnum::kRm, [common]() -> SourceOperandInterface* {
+    int rm = Extractors::RType::ExtractFunc3(common->inst_word());
+    switch (rm) {
+      case 0:
+        return new IntLiteralOperand<0>();
+      case 1:
+        return new IntLiteralOperand<1>();
+      case 2:
+        return new IntLiteralOperand<2>();
+      case 3:
+        return new IntLiteralOperand<3>();
+      case 4:
+        return new IntLiteralOperand<4>();
+      case 5:
+        return new IntLiteralOperand<5>();
+      case 6:
+        return new IntLiteralOperand<6>();
+      case 7:
+        return new IntLiteralOperand<7>();
+      default:
+        return nullptr;
+    }
+  });
+  Insert(getter_map, *SourceOpEnum::kRs1,
+         [common]() -> SourceOperandInterface* {
+           int num = Extractors::RType::ExtractRs1(common->inst_word());
+           if (num == 0) return new IntLiteralOperand<0>({1});
+           return GetRegisterSourceOp<RV32Register>(
+               common->state(), absl::StrCat(RiscVState::kXregPrefix, num),
+               kXRegisterAliases[num]);
+         });
+  Insert(getter_map, *SourceOpEnum::kRs2,
+         [common]() -> SourceOperandInterface* {
+           int num = Extractors::RType::ExtractRs2(common->inst_word());
+           if (num == 0) return new IntLiteralOperand<0>({1});
+           return GetRegisterSourceOp<RV32Register>(
+               common->state(), absl::StrCat(RiscVState::kXregPrefix, num),
+               kXRegisterAliases[num]);
+         });
+  Insert(getter_map, *SourceOpEnum::kSImm12,
+         [common]() -> SourceOperandInterface* {
+           return new ImmediateOperand<int32_t>(
+               Extractors::SType::ExtractSImm(common->inst_word()));
+         });
+  Insert(getter_map, *SourceOpEnum::kSimm5,
+         [common]() -> SourceOperandInterface* {
+           return new ImmediateOperand<int32_t>(
+               Extractors::VArith::ExtractSimm5(common->inst_word()));
+         });
+  Insert(getter_map, *SourceOpEnum::kSucc,
+         [common]() -> SourceOperandInterface* {
+           return new ImmediateOperand<uint32_t>(
+               Extractors::Fence::ExtractSucc(common->inst_word()));
+         });
+  Insert(getter_map, *SourceOpEnum::kUImm20,
+         [common]() -> SourceOperandInterface* {
+           return new ImmediateOperand<int32_t>(
+               Extractors::UType::ExtractUImm(common->inst_word()));
+         });
+  Insert(getter_map, *SourceOpEnum::kUimm5,
+         [common]() -> SourceOperandInterface* {
+           return new ImmediateOperand<int32_t>(
+               Extractors::VArith::ExtractUimm5(common->inst_word()));
+         });
+  Insert(getter_map, *SourceOpEnum::kVd, [common]() -> SourceOperandInterface* {
+    return GetVectorRegisterSourceOp<RVVectorRegister>(
+        common->state(), Extractors::VArith::ExtractVd(common->inst_word()));
+  });
+  Insert(getter_map, *SourceOpEnum::kVm, [common]() -> SourceOperandInterface* {
+    int vm = Extractors::VArith::ExtractVm(common->inst_word());
+    return new ImmediateOperand<bool>(vm, absl::StrCat("vm.", vm ? "t" : "f"));
+  });
+  Insert(getter_map, *SourceOpEnum::kVmask,
+         [common]() -> SourceOperandInterface* {
+           int vm = Extractors::VArith::ExtractVm(common->inst_word());
+           if (vm) {
+             // Unmasked, return the True mask.
+             return new RV32VectorTrueOperand(common->state());
+           }
+           // Masked. Return the mask register.
+           return GetVectorMaskRegisterSourceOp<RVVectorRegister>(
+               common->state(), 0);
+         });
+  Insert(getter_map, *SourceOpEnum::kVmaskTrue,
+         [common]() -> SourceOperandInterface* {
+           return new RV32VectorTrueOperand(common->state());
+         });
+  Insert(getter_map, *SourceOpEnum::kVs1,
+         [common]() -> SourceOperandInterface* {
+           int num = Extractors::VArith::ExtractVs1(common->inst_word());
+           return GetVectorRegisterSourceOp<RVVectorRegister>(common->state(),
+                                                              num);
+         });
+  Insert(getter_map, *SourceOpEnum::kVs2,
+         [common]() -> SourceOperandInterface* {
+           int num = Extractors::VArith::ExtractVs2(common->inst_word());
+           return GetVectorRegisterSourceOp<RVVectorRegister>(common->state(),
+                                                              num);
+         });
+  Insert(getter_map, *SourceOpEnum::kVs3,
+         [common]() -> SourceOperandInterface* {
+           int num = Extractors::VMem::ExtractVs3(common->inst_word());
+           return GetVectorRegisterSourceOp<RVVectorRegister>(common->state(),
+                                                              num);
+         });
+  Insert(getter_map, *SourceOpEnum::kZimm10,
+         [common]() -> SourceOperandInterface* {
+           int num = Extractors::VConfig::ExtractZimm10(common->inst_word());
+           return new ImmediateOperand<int32_t>(num);
+         });
+  Insert(getter_map, *SourceOpEnum::kZimm11,
+         [common]() -> SourceOperandInterface* {
+           int num = Extractors::VConfig::ExtractZimm11(common->inst_word());
+           return new ImmediateOperand<int32_t>(num);
+         });
+}
+
+template <typename DestOpEnum, typename Extractors>
+void AddKelvinV2DestGetters(DestOpGetterMap& getter_map,
+                            RiscVEncodingCommon* /*absl_nonnull*/ common) {
+  // Destination operand getters.
+  Insert(getter_map, *DestOpEnum::kCsr,
+         [common](int latency) -> DestinationOperandInterface* {
+           return GetRegisterDestinationOp<RV32Register>(
+               common->state(), RiscVState::kCsrName, latency);
+         });
+  Insert(getter_map, *DestOpEnum::kFflags,
+         [common](int latency) -> DestinationOperandInterface* {
+           return GetCSRSetBitsDestinationOp<uint32_t>(common->state(),
+                                                       "fflags", latency, "");
+         });
+  Insert(getter_map, *DestOpEnum::kFrd,
+         [common](int latency) -> DestinationOperandInterface* {
+           int num = Extractors::RType::ExtractRd(common->inst_word());
+           return GetRegisterDestinationOp<RVFpRegister>(
+               common->state(), absl::StrCat(RiscVState::kFregPrefix, num),
+               latency, kFRegisterAliases[num]);
+         });
+  Insert(getter_map, *DestOpEnum::kNextPc,
+         [common](int latency) -> DestinationOperandInterface* {
+           return GetRegisterDestinationOp<RV32Register>(
+               common->state(), RiscVState::kPcName, latency);
+         });
+  Insert(getter_map, *DestOpEnum::kRd,
+         [common](int latency) -> DestinationOperandInterface* {
+           int num = Extractors::RType::ExtractRd(common->inst_word());
+           if (num == 0) {
+             return GetRegisterDestinationOp<RV32Register>(common->state(),
+                                                           "X0Dest", 0);
+           } else {
+             return GetRegisterDestinationOp<RV32Register>(
+                 common->state(), absl::StrCat(RiscVState::kXregPrefix, num),
+                 latency, kXRegisterAliases[num]);
+           }
+         });
+  Insert(getter_map, *DestOpEnum::kVd,
+         [common](int latency) -> DestinationOperandInterface* {
+           int num = Extractors::VArith::ExtractVd(common->inst_word());
+           return GetVectorRegisterDestinationOp<RVVectorRegister>(
+               common->state(), latency, num);
+         });
+}
+
+}  // namespace kelvin::sim
+
+#endif  // SIM_KELVIN_V2_GETTERS_H_
diff --git a/sim/kelvin_v2_state.cc b/sim/kelvin_v2_state.cc
new file mode 100644
index 0000000..b930463
--- /dev/null
+++ b/sim/kelvin_v2_state.cc
@@ -0,0 +1,48 @@
+// 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 "sim/kelvin_v2_state.h"
+
+#include <cstdint>
+
+#include "absl/base/nullability.h"
+#include "absl/strings/string_view.h"
+#include "riscv/riscv_state.h"
+#include "mpact/sim/util/memory/memory_interface.h"
+
+namespace kelvin::sim {
+using ::mpact::sim::riscv::RiscVXlen;
+using ::mpact::sim::util::AtomicMemoryOpInterface;
+using ::mpact::sim::util::MemoryInterface;
+
+// StretchMisa32 stretches the 32-bit value into a 64-bit value by moving the
+// upper 2 bits to the lower 32 bits.
+static inline uint64_t StretchMisa32(uint32_t value) {
+  uint64_t value64 = static_cast<uint64_t>(value);
+  value64 = ((value64 & 0xc000'0000) << 32) | (value64 & 0x03ff'ffff);
+  return value64;
+}
+
+constexpr uint32_t kKelvinV2MisaInitialValue = 0x40201120;
+
+KelvinV2State::KelvinV2State(
+    absl::string_view id, RiscVXlen xlen, MemoryInterface* /*absl_nonnull*/ memory,
+    AtomicMemoryOpInterface* /*absl_nullable*/ atomic_memory)
+    : RiscVState(id, xlen, memory, atomic_memory) {
+  // Set the initial value of the misa CSR to the Kelvin V2 ISA value.
+  misa()->Set(StretchMisa32(kKelvinV2MisaInitialValue));
+}
+KelvinV2State::~KelvinV2State() = default;
+
+}  // namespace kelvin::sim
diff --git a/sim/kelvin_v2_state.h b/sim/kelvin_v2_state.h
new file mode 100644
index 0000000..33d7dd7
--- /dev/null
+++ b/sim/kelvin_v2_state.h
@@ -0,0 +1,47 @@
+// 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 SIM_KELVIN_V2_STATE_H_
+#define SIM_KELVIN_V2_STATE_H_
+
+#include "absl/base/nullability.h"
+#include "absl/strings/string_view.h"
+#include "riscv/riscv_state.h"
+#include "mpact/sim/util/memory/memory_interface.h"
+
+namespace kelvin::sim {
+class KelvinV2State : public ::mpact::sim::riscv::RiscVState {
+ public:
+  using AtomicMemoryOpInterface = ::mpact::sim::util::AtomicMemoryOpInterface;
+  using MemoryInterface = ::mpact::sim::util::MemoryInterface;
+  using RiscVState = ::mpact::sim::riscv::RiscVState;
+  using RiscVXlen = ::mpact::sim::riscv::RiscVXlen;
+
+  KelvinV2State(absl::string_view id, RiscVXlen xlen,
+                MemoryInterface* /*absl_nonnull*/ memory,
+                AtomicMemoryOpInterface* /*absl_nullable*/ atomic_memory);
+  KelvinV2State(absl::string_view id, RiscVXlen xlen,
+                MemoryInterface* /*absl_nonnull*/ memory)
+      : KelvinV2State(id, xlen, memory, nullptr) {}
+  ~KelvinV2State() override;
+
+  // Deleted Constructors and operators.
+  KelvinV2State(const KelvinV2State&) = delete;
+  KelvinV2State(KelvinV2State&&) = delete;
+  KelvinV2State& operator=(const KelvinV2State&) = delete;
+  KelvinV2State& operator=(KelvinV2State&&) = delete;
+};
+}  // namespace kelvin::sim
+
+#endif  // SIM_KELVIN_V2_STATE_H_
diff --git a/sim/kelvin_v2_user_decoder.cc b/sim/kelvin_v2_user_decoder.cc
new file mode 100644
index 0000000..bd1428d
--- /dev/null
+++ b/sim/kelvin_v2_user_decoder.cc
@@ -0,0 +1,97 @@
+// 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 "sim/kelvin_v2_user_decoder.h"
+
+#include <cstdint>
+#include <memory>
+
+#include "sim/kelvin_v2_decoder.h"
+#include "sim/kelvin_v2_encoding.h"
+#include "sim/kelvin_v2_enums.h"
+#include "sim/kelvin_v2_state.h"
+#include "absl/base/nullability.h"
+#include "riscv/riscv_state.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/program_error.h"
+#include "mpact/sim/util/memory/memory_interface.h"
+
+namespace kelvin::sim {
+using ::kelvin::sim::KelvinV2Encoding;
+using ::kelvin::sim::KelvinV2State;
+using ::kelvin::sim::isa32_v2::KelvinV2InstructionSet;
+using ::mpact::sim::generic::Instruction;
+using ::mpact::sim::generic::ProgramErrorController;
+using ::mpact::sim::generic::operator*;  // NOLINT
+using ::kelvin::sim::isa32_v2::kOpcodeNames;
+using ::kelvin::sim::isa32_v2::OpcodeEnum;
+using ::mpact::sim::riscv::ExceptionCode;
+using ::mpact::sim::util::MemoryInterface;
+
+KelvinV2UserDecoder::KelvinV2UserDecoder(KelvinV2State* /*absl_nonnull*/ state,
+                                         MemoryInterface* /*absl_nonnull*/ memory)
+    : state_(state), memory_(memory) {
+  // Need a data buffer to load instructions from memory. Allocate a single
+  // buffer that can be reused for each instruction word.
+  inst_db_ = state_->db_factory()->Allocate<uint32_t>(1);
+  // Allocate the isa factory class, the top level isa decoder instance, and
+  // the encoding parser.
+  kelvin_v2_isa_factory_ = std::make_unique<KelvinV2IsaFactory>();
+  kelvin_v2_isa_ = std::make_unique<KelvinV2InstructionSet>(
+      state, kelvin_v2_isa_factory_.get());
+  kelvin_v2_encoding_ = std::make_unique<KelvinV2Encoding>(state);
+  decode_error_ = state->program_error_controller()->GetProgramError(
+      ProgramErrorController::kInternalErrorName);
+}
+
+KelvinV2UserDecoder::~KelvinV2UserDecoder() { inst_db_->DecRef(); }
+
+Instruction* KelvinV2UserDecoder::DecodeInstruction(uint64_t address) {
+  // Address alignment check.
+  if (address & 0x1) {
+    Instruction* inst = new Instruction(0, state_);
+    inst->set_size(1);
+    inst->SetDisassemblyString("Misaligned instruction address");
+    inst->set_opcode(*::kelvin::sim::isa32_v2::OpcodeEnum::kNone);
+    inst->set_address(address);
+    inst->set_semantic_function([this](Instruction* inst) {
+      state_->Trap(/*is_interrupt*/ false, inst->address(),
+                   *ExceptionCode::kInstructionAddressMisaligned,
+                   inst->address() ^ 0x1, inst);
+    });
+    return inst;
+  }
+
+  // TODO - b/442008530: Trigger a decoder failure if address is outside the
+  //                     ITCM range.
+
+  // Read the instruction word from memory and parse it in the encoding parser.
+  memory_->Load(address, inst_db_, nullptr, nullptr);
+  uint32_t iword = inst_db_->Get<uint32_t>(0);
+  kelvin_v2_encoding_->ParseInstruction(iword);
+
+  // Call the isa decoder to obtain a new instruction object for the instruction
+  // word that was parsed above.
+  return kelvin_v2_isa_->Decode(address, kelvin_v2_encoding_.get());
+}
+
+int KelvinV2UserDecoder::GetNumOpcodes() const {
+  return static_cast<int>(OpcodeEnum::kPastMaxValue);
+}
+
+const char* KelvinV2UserDecoder::GetOpcodeName(int index) const {
+  return kOpcodeNames[index];
+}
+
+}  // namespace kelvin::sim
diff --git a/sim/kelvin_v2_user_decoder.h b/sim/kelvin_v2_user_decoder.h
new file mode 100644
index 0000000..9cc83e7
--- /dev/null
+++ b/sim/kelvin_v2_user_decoder.h
@@ -0,0 +1,83 @@
+// 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 SIM_KELVIN_V2_USER_DECODER_H_
+#define SIM_KELVIN_V2_USER_DECODER_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "sim/kelvin_v2_decoder.h"
+#include "sim/kelvin_v2_encoding.h"
+#include "sim/kelvin_v2_state.h"
+#include "absl/base/nullability.h"
+#include "mpact/sim/generic/arch_state.h"
+#include "mpact/sim/generic/data_buffer.h"
+#include "mpact/sim/generic/decoder_interface.h"
+#include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/generic/program_error.h"
+#include "mpact/sim/util/memory/memory_interface.h"
+
+namespace kelvin::sim {
+
+// This is the factory class needed by the generated decoder. It is responsible
+// for creating the decoder for each slot instance. Since the riscv architecture
+// only has a single slot, it's a pretty simple class.
+class KelvinV2IsaFactory
+    : public ::kelvin::sim::isa32_v2::KelvinV2InstructionSetFactory {
+  using ArchState = ::mpact::sim::generic::ArchState;
+  using KelvinV2Slot = ::kelvin::sim::isa32_v2::KelvinV2Slot;
+
+ public:
+  std::unique_ptr<KelvinV2Slot> CreateKelvinV2Slot(ArchState* state) override {
+    return std::make_unique<KelvinV2Slot>(state);
+  }
+};
+
+class KelvinV2UserDecoder : public ::mpact::sim::generic::DecoderInterface {
+ public:
+  using DataBuffer = ::mpact::sim::generic::DataBuffer;
+  using Instruction = ::mpact::sim::generic::Instruction;
+  using KelvinV2Encoding = ::kelvin::sim::KelvinV2Encoding;
+  using KelvinV2InstructionSet =
+      ::kelvin::sim::isa32_v2::KelvinV2InstructionSet;
+  using MemoryInterface = ::mpact::sim::util::MemoryInterface;
+  using ProgramError = ::mpact::sim::generic::ProgramError;
+
+  KelvinV2UserDecoder(KelvinV2State* /*absl_nonnull*/ state,
+                      MemoryInterface* /*absl_nonnull*/ memory);
+  ~KelvinV2UserDecoder() override;
+
+  // Decodes an instruction at the given address.
+  Instruction* DecodeInstruction(uint64_t address) override;
+
+  // Returns the number of opcodes supported by this decoder.
+  int GetNumOpcodes() const override;
+
+  // Returns the name of the opcode at the given index.
+  const char* GetOpcodeName(int index) const override;
+
+ private:
+  KelvinV2State* state_;
+  MemoryInterface* memory_;
+  DataBuffer* inst_db_;
+  std::unique_ptr<ProgramError> decode_error_;
+  std::unique_ptr<KelvinV2Encoding> kelvin_v2_encoding_;
+  std::unique_ptr<KelvinV2IsaFactory> kelvin_v2_isa_factory_;
+  std::unique_ptr<KelvinV2InstructionSet> kelvin_v2_isa_;
+};
+
+}  // namespace kelvin::sim
+
+#endif  // SIM_KELVIN_V2_USER_DECODER_H_
diff --git a/sim/test/BUILD b/sim/test/BUILD
index f966a87..920d836 100644
--- a/sim/test/BUILD
+++ b/sim/test/BUILD
@@ -206,8 +206,25 @@
 )
 
 cc_test(
+    name = "kelvin_v2_user_decoder_test",
+    srcs = ["kelvin_v2_user_decoder_test.cc"],
+    copts = ["-Werror"],
+    deps = [
+        "//sim:kelvin_v2_isa",
+        "//sim:kelvin_v2_state",
+        "//sim:kelvin_v2_user_decoder",
+        "@com_google_absl//absl/memory",
+        "@com_google_googletest//:gtest_main",
+        "@com_google_mpact-riscv//riscv:riscv_state",
+        "@com_google_mpact-sim//mpact/sim/generic:core",
+        "@com_google_mpact-sim//mpact/sim/util/memory",
+    ],
+)
+
+cc_test(
     name = "kelvin_cosim_dpi_wrapper_test",
     srcs = ["kelvin_cosim_dpi_wrapper_test.cc"],
+    copts = ["-Werror"],
     deps = [
         "//sim/cosim:kelvin_cosim_lib",
         "@com_google_googletest//:gtest_main",
diff --git a/sim/test/kelvin_cosim_dpi_wrapper_test.cc b/sim/test/kelvin_cosim_dpi_wrapper_test.cc
index 0c5fca5..035230f 100644
--- a/sim/test/kelvin_cosim_dpi_wrapper_test.cc
+++ b/sim/test/kelvin_cosim_dpi_wrapper_test.cc
@@ -6,61 +6,103 @@
 
 namespace {
 
-const uint32_t kLoadImmediateToX5 = 0b11011110101011011011'00101'0110111;
-const uint32_t kAddImmediateToX5_2047 = 0b011111111111'00101'000'00101'0010011;
-const uint32_t kAddImmediateToX5_1776 = 0b011011110000'00101'000'00101'0010011;
-const uint32_t kExpectedX5Value = 0xdeadbeef;
-const uint32_t kNopInstruction = 0x00000013;  // x0 = x0 + 0 (nop)
-const uint32_t kMcycleCsrAddress = 0xb00;
+constexpr uint32_t kLoadImmediateToX5 = 0b11011110101011011011'00101'0110111;
+constexpr uint32_t kFmvX5ToF5 = 0b1111000'00000'00101'000'00101'1010011;
+constexpr uint32_t kAddImmediateToX5_2047 =
+    0b011111111111'00101'000'00101'0010011;
+constexpr uint32_t kAddImmediateToX5_1776 =
+    0b011011110000'00101'000'00101'0010011;
+constexpr uint32_t kExpectedX5Value = 0xdeadbeef;
+constexpr uint32_t kNopInstruction = 0x00000013;  // x0 = x0 + 0 (nop)
+constexpr uint32_t kExpectedMisaValue = 0x40201120;
 
 class CosimFixture : public ::testing::Test {
  public:
   CosimFixture() { mpact_init(); }
   ~CosimFixture() override { mpact_fini(); }
+
+  int add_test_values_to_x5() {
+    int status = 0;
+    status = mpact_step_wrapper(kLoadImmediateToX5);
+    if (status != 0) {
+      return status;
+    }
+    status = mpact_step_wrapper(kAddImmediateToX5_2047);
+    if (status != 0) {
+      return status;
+    }
+    status = mpact_step_wrapper(kAddImmediateToX5_1776);
+    return status;
+  }
+
+  int mpact_step_wrapper(uint32_t instruction) {
+    int status = 0;
+    svLogicVecVal instruction_struct;
+    instruction_struct.aval = instruction;
+    instruction_struct.bval = 0;
+    status = mpact_step(&instruction_struct);
+    return status;
+  }
 };
 
-TEST_F(CosimFixture, Step) {
-  svLogicVecVal instruction;
-  instruction.aval = 0x00000000;
-  EXPECT_EQ(mpact_step(&instruction), 0);
+TEST_F(CosimFixture, GetPc) {
+  uint32_t pc_value = 1;
+  EXPECT_EQ(mpact_get_register("pc", &pc_value), 0);
+  EXPECT_EQ(pc_value, 0);
 }
 
-TEST_F(CosimFixture, GetPc) { EXPECT_EQ(mpact_get_pc(), 0); }
-
 TEST_F(CosimFixture, GetPcAfterStep) {
-  svLogicVecVal instruction;
-  instruction.aval = kNopInstruction;
-  EXPECT_EQ(mpact_step(&instruction), 0);
-  EXPECT_EQ(mpact_get_pc(), 4);
+  uint32_t pc_value = 1;
+  EXPECT_EQ(mpact_step_wrapper(kNopInstruction), 0);
+  EXPECT_EQ(mpact_get_register("pc", &pc_value), 0);
+  EXPECT_EQ(pc_value, 4);
 }
 
 TEST_F(CosimFixture, GetPcAfterReset) {
-  svLogicVecVal instruction;
-  instruction.aval = kNopInstruction;  // x0 = x0 + 0 (nop)
-  EXPECT_EQ(mpact_step(&instruction), 0);
-  EXPECT_NE(mpact_get_pc(), 0);
+  uint32_t pc_value = 1;
+  EXPECT_EQ(mpact_step_wrapper(kNopInstruction), 0);
+  EXPECT_EQ(mpact_get_register("pc", &pc_value), 0);
+  EXPECT_NE(pc_value, 0);
   EXPECT_EQ(mpact_reset(), 0);
-  EXPECT_EQ(mpact_get_pc(), 0);
+  EXPECT_EQ(mpact_get_register("pc", &pc_value), 0);
+  EXPECT_EQ(pc_value, 0);
 }
 
-TEST_F(CosimFixture, CheckGpr) {
-  EXPECT_EQ(mpact_get_gpr(5), 0);
-  svLogicVecVal instruction;
-  instruction.aval = kLoadImmediateToX5;
-  EXPECT_EQ(mpact_step(&instruction), 0);
-  instruction.aval = kAddImmediateToX5_2047;
-  EXPECT_EQ(mpact_step(&instruction), 0);
-  instruction.aval = kAddImmediateToX5_1776;
-  EXPECT_EQ(mpact_step(&instruction), 0);
-  EXPECT_EQ(mpact_get_gpr(5), kExpectedX5Value);
+TEST_F(CosimFixture, GetGpr) {
+  uint32_t gpr_value = 1;
+  EXPECT_EQ(mpact_get_register("x5", &gpr_value), 0);
+  EXPECT_EQ(gpr_value, 0);
+  EXPECT_EQ(add_test_values_to_x5(), 0);
+  EXPECT_EQ(mpact_get_register("x5", &gpr_value), 0);
+  EXPECT_EQ(gpr_value, kExpectedX5Value);
 }
 
 TEST_F(CosimFixture, GetMcycleCsr) {
-  EXPECT_EQ(mpact_get_csr(kMcycleCsrAddress), 0);
-  svLogicVecVal instruction;
-  instruction.aval = kNopInstruction;  // x0 = x0 + 0 (nop)
-  EXPECT_EQ(mpact_step(&instruction), 0);
-  EXPECT_EQ(mpact_get_csr(kMcycleCsrAddress), 1);
+  uint32_t mcycle_value = 12345;
+  EXPECT_EQ(mpact_get_register("mcycle", &mcycle_value), 0);
+  EXPECT_EQ(mcycle_value, 0);
+  EXPECT_EQ(mpact_step_wrapper(kNopInstruction), 0);
+  EXPECT_EQ(mpact_get_register("mcycle", &mcycle_value), 0);
+  EXPECT_EQ(mcycle_value, 1);
+}
+
+TEST_F(CosimFixture, GetFpr) {
+  uint32_t gpr_value = 1;
+  uint32_t fpr_value = 1;
+  EXPECT_EQ(mpact_get_register("x5", &gpr_value), 0);
+  EXPECT_EQ(gpr_value, 0);
+  EXPECT_EQ(mpact_get_register("f5", &fpr_value), 0);
+  EXPECT_EQ(fpr_value, 0);
+  EXPECT_EQ(add_test_values_to_x5(), 0);
+  EXPECT_EQ(mpact_step_wrapper(kFmvX5ToF5), 0);
+  EXPECT_EQ(mpact_get_register("f5", &fpr_value), 0);
+  EXPECT_EQ(fpr_value, kExpectedX5Value);
+}
+
+TEST_F(CosimFixture, GetMisaCsr) {
+  uint32_t misa_value = 0;
+  EXPECT_EQ(mpact_get_register("misa", &misa_value), 0);
+  EXPECT_EQ(misa_value, kExpectedMisaValue);
 }
 
 }  // namespace
diff --git a/sim/test/kelvin_v2_user_decoder_test.cc b/sim/test/kelvin_v2_user_decoder_test.cc
new file mode 100644
index 0000000..d7111b9
--- /dev/null
+++ b/sim/test/kelvin_v2_user_decoder_test.cc
@@ -0,0 +1,113 @@
+// 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 "sim/kelvin_v2_user_decoder.h"
+
+#include <cstdint>
+#include <memory>
+
+#include "sim/kelvin_v2_encoding.h"
+#include "sim/kelvin_v2_enums.h"
+#include "sim/kelvin_v2_state.h"
+#include "googletest/include/gtest/gtest.h"
+#include "absl/memory/memory.h"
+#include "riscv/riscv_state.h"
+#include "mpact/sim/generic/data_buffer.h"
+#include "mpact/sim/util/memory/flat_demand_memory.h"
+#include "mpact/sim/util/memory/memory_interface.h"
+
+namespace {
+
+using ::kelvin::sim::KelvinV2Encoding;
+using ::kelvin::sim::KelvinV2State;
+using ::kelvin::sim::KelvinV2UserDecoder;
+using ::kelvin::sim::isa32_v2::DestOpEnum;
+using ::kelvin::sim::isa32_v2::kDestOpNames;
+using ::kelvin::sim::isa32_v2::kSourceOpNames;
+using ::kelvin::sim::isa32_v2::OpcodeEnum;
+using ::kelvin::sim::isa32_v2::SourceOpEnum;
+using ::mpact::sim::generic::DataBuffer;
+using ::mpact::sim::riscv::Instruction;
+using ::mpact::sim::riscv::RiscVXlen;
+using ::mpact::sim::generic::operator*;  // NOLINT: clang-tidy false positive.
+using ::mpact::sim::util::FlatDemandMemory;
+using ::mpact::sim::util::MemoryInterface;
+
+// addi x1, x1, 0
+constexpr uint32_t kNopAddiInstruction = 0b000000000000'00001'000'00001'0010011;
+
+class KelvinV2UserDecoderFixture : public ::testing::Test {
+ public:
+  void SetUp() override {
+    memory_ = std::make_unique<FlatDemandMemory>();
+    state_ = std::make_unique<KelvinV2State>("KelvinV2", RiscVXlen::RV32,
+                                             memory_.get());
+    decoder_ =
+        std::make_unique<KelvinV2UserDecoder>(state_.get(), memory_.get());
+  }
+
+ protected:
+  std::unique_ptr<KelvinV2State> state_;
+  std::unique_ptr<MemoryInterface> memory_;
+  std::unique_ptr<KelvinV2UserDecoder> decoder_;
+};
+
+TEST_F(KelvinV2UserDecoderFixture, TestGetNumOpcodes) {
+  EXPECT_NE(decoder_->GetNumOpcodes(), 0);
+}
+
+TEST_F(KelvinV2UserDecoderFixture, DecodeInstruction) {
+  uint64_t test_address = 0;
+  DataBuffer* inst_db = state_->db_factory()->Allocate<uint32_t>(1);
+  inst_db->Set<uint32_t>(/*index=*/0, kNopAddiInstruction);
+  memory_->Store(test_address, inst_db);
+  std::unique_ptr<Instruction> instruction =
+      absl::WrapUnique(decoder_->DecodeInstruction(test_address));
+  EXPECT_NE(instruction.get(), nullptr);
+  EXPECT_EQ(instruction->opcode(), *OpcodeEnum::kAddi);
+  inst_db->DecRef();
+}
+
+class KelvinV2EncodingFixture : public ::testing::Test {
+ public:
+  void SetUp() override {
+    memory_ = std::make_unique<FlatDemandMemory>();
+    state_ = std::make_unique<KelvinV2State>("KelvinV2", RiscVXlen::RV32,
+                                             memory_.get());
+    encoding_ = std::make_unique<KelvinV2Encoding>(state_.get());
+  }
+
+ protected:
+  std::unique_ptr<KelvinV2State> state_;
+  std::unique_ptr<MemoryInterface> memory_;
+  std::unique_ptr<KelvinV2Encoding> encoding_;
+};
+
+TEST_F(KelvinV2EncodingFixture, AllSourceOpsHaveGetters) {
+  for (int i = *SourceOpEnum::kNone; i < *SourceOpEnum::kPastMaxValue; i++) {
+    EXPECT_TRUE(encoding_->source_op_getters().contains(i))
+        << "No source operand for enum value " << i << " (" << kSourceOpNames[i]
+        << ")";
+  }
+}
+
+TEST_F(KelvinV2EncodingFixture, AllDestOpsHaveGetters) {
+  for (int i = *DestOpEnum::kNone; i < *DestOpEnum::kPastMaxValue; i++) {
+    EXPECT_TRUE(encoding_->dest_op_getters().contains(i))
+        << "No dest operand for enum value " << i << " (" << kDestOpNames[i]
+        << ")";
+  }
+}
+
+}  // namespace