Changed interface to riscv_top/cheriot_top to make it easier to reuse the class.

PiperOrigin-RevId: 643133613
Change-Id: I8a6e201483394e112e85cc9ff7e2361a81d183bc
diff --git a/cheriot/BUILD b/cheriot/BUILD
index 17501f1..e2750df 100644
--- a/cheriot/BUILD
+++ b/cheriot/BUILD
@@ -220,6 +220,7 @@
         ":debug_command_shell",
         ":instrumentation",
         ":riscv_cheriot",
+        ":riscv_cheriot_decoder",
         "@com_google_absl//absl/flags:flag",
         "@com_google_absl//absl/flags:parse",
         "@com_google_absl//absl/flags:usage",
@@ -337,6 +338,7 @@
         ":debug_command_shell",
         ":instrumentation",
         ":riscv_cheriot",
+        ":riscv_cheriot_decoder",
         "@com_google_absl//absl/functional:any_invocable",
         "@com_google_absl//absl/functional:bind_front",
         "@com_google_absl//absl/log",
diff --git a/cheriot/cheriot_decoder.cc b/cheriot/cheriot_decoder.cc
index ba5e4d7..63e6103 100644
--- a/cheriot/cheriot_decoder.cc
+++ b/cheriot/cheriot_decoder.cc
@@ -44,7 +44,7 @@
 
   // 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);
+  inst_db_ = db_factory_.Allocate<uint32_t>(1);
   // Allocate the isa factory class, the top level isa decoder instance, and
   // the encoding parser.
   cheriot_isa_factory_ = new CheriotIsaFactory();
diff --git a/cheriot/cheriot_decoder.h b/cheriot/cheriot_decoder.h
index 93442f3..7912734 100644
--- a/cheriot/cheriot_decoder.h
+++ b/cheriot/cheriot_decoder.h
@@ -64,6 +64,15 @@
   // instance will raise an internal simulator error when executed.
   generic::Instruction *DecodeInstruction(uint64_t address) override;
 
+  // Return the number of opcodes supported by this decoder.
+  int GetNumOpcodes() const override {
+    return static_cast<int>(OpcodeEnum::kPastMaxValue);
+  }
+  // Return the name of the opcode at the given index.
+  const char *GetOpcodeName(int index) const override {
+    return isa32::kOpcodeNames[index];
+  }
+
   // Getter.
   isa32::RiscVCheriotEncoding *cheriot_encoding() const {
     return cheriot_encoding_;
@@ -73,6 +82,7 @@
   CheriotState *state_;
   util::MemoryInterface *memory_;
   std::unique_ptr<generic::ProgramError> decode_error_;
+  generic::DataBufferFactory db_factory_;
   generic::DataBuffer *inst_db_;
   isa32::RiscVCheriotEncoding *cheriot_encoding_;
   isa32::RiscVCheriotInstructionSetFactory *cheriot_isa_factory_;
diff --git a/cheriot/cheriot_renode.cc b/cheriot/cheriot_renode.cc
index aad6ac0..e308fa9 100644
--- a/cheriot/cheriot_renode.cc
+++ b/cheriot/cheriot_renode.cc
@@ -35,6 +35,7 @@
 #include "cheriot/cheriot_cli_forwarder.h"
 #include "cheriot/cheriot_debug_info.h"
 #include "cheriot/cheriot_debug_interface.h"
+#include "cheriot/cheriot_decoder.h"
 #include "cheriot/cheriot_instrumentation_control.h"
 #include "cheriot/cheriot_renode_cli_top.h"
 #include "cheriot/cheriot_renode_register_info.h"
@@ -104,10 +105,13 @@
   mem_profiler_ = new TaggedMemoryUseProfiler(data_memory);
   data_memory = mem_profiler_;
   mem_profiler_->set_is_enabled(false);
+  cheriot_state_ = new CheriotState(
+      "CherIoT", data_memory, static_cast<AtomicMemoryOpInterface *>(router_));
+  cheriot_decoder_ = new CheriotDecoder(
+      cheriot_state_, static_cast<MemoryInterface *>(router_));
+
   // Instantiate cheriot_top.
-  cheriot_top_ =
-      new CheriotTop(name, static_cast<MemoryInterface *>(router_), data_memory,
-                     static_cast<MemoryInterface *>(router_));
+  cheriot_top_ = new CheriotTop("Cheriot", cheriot_state_, cheriot_decoder_);
   // Initialize minstret/minstreth. Bind the instruction counter to those
   // registers.
   auto minstret_res = cheriot_top_->state()->csr_set()->GetCsr("minstret");
@@ -142,9 +146,10 @@
   CHECK_OK(renode_router_->AddDefaultTarget<MemoryInterface>(tagged_memory_));
 
   // Set up semihosting.
-  semihost_ = new RiscVArmSemihost(RiscVArmSemihost::BitWidth::kWord32,
-                                   cheriot_top_->inst_memory(),
-                                   cheriot_top_->data_memory());
+  semihost_ =
+      new RiscVArmSemihost(RiscVArmSemihost::BitWidth::kWord32,
+                           static_cast<MemoryInterface *>(router_),
+                           static_cast<MemoryInterface *>(renode_router_));
   // Set up special handlers (ebreak, wfi, ecall).
   cheriot_top_->state()->AddEbreakHandler([this](const Instruction *inst) {
     if (this->semihost_->IsSemihostingCall(inst)) {
@@ -217,6 +222,8 @@
   delete socket_cli_;
   delete cheriot_renode_cli_top_;
   delete cheriot_cli_forwarder_;
+  delete cheriot_decoder_;
+  delete cheriot_state_;
   delete cheriot_top_;
   delete semihost_;
   delete router_;
diff --git a/cheriot/cheriot_renode.h b/cheriot/cheriot_renode.h
index 377c01f..0f2717c 100644
--- a/cheriot/cheriot_renode.h
+++ b/cheriot/cheriot_renode.h
@@ -24,8 +24,10 @@
 #include "absl/status/status.h"
 #include "absl/status/statusor.h"
 #include "cheriot/cheriot_cli_forwarder.h"
+#include "cheriot/cheriot_decoder.h"
 #include "cheriot/cheriot_instrumentation_control.h"
 #include "cheriot/cheriot_renode_cli_top.h"
+#include "cheriot/cheriot_state.h"
 #include "cheriot/cheriot_top.h"
 #include "cheriot/debug_command_shell.h"
 #include "mpact/sim/generic/core_debug_interface.h"
@@ -138,6 +140,8 @@
   std::string name_;
   MemoryInterface *renode_sysbus_ = nullptr;
   TaggedMemoryInterface *tagged_sysbus_ = nullptr;
+  CheriotState *cheriot_state_ = nullptr;
+  CheriotDecoder *cheriot_decoder_ = nullptr;
   CheriotTop *cheriot_top_ = nullptr;
   RiscVArmSemihost *semihost_ = nullptr;
   SingleInitiatorRouter *router_ = nullptr;
diff --git a/cheriot/cheriot_state.cc b/cheriot/cheriot_state.cc
index 525c5c8..1842f29 100644
--- a/cheriot/cheriot_state.cc
+++ b/cheriot/cheriot_state.cc
@@ -289,11 +289,6 @@
   // Create the other CSRs.
   csr_set_ = new RiscVCsrSet();
   CreateCsrs<uint32_t>(this, csr_vec_);
-  if (tagged_memory_ == nullptr) {
-    owned_tagged_memory_ = new util::TaggedFlatDemandMemory(
-        CheriotRegister::kCapabilitySizeInBytes);
-    tagged_memory_ = owned_tagged_memory_;
-  }
   pc_src_operand_ = new RiscVCheri32PcSourceOperand(this);
   set_pc_operand(pc_src_operand_);
   // Create the revocation data buffer.
@@ -308,7 +303,6 @@
   delete executable_root_;
   delete sealing_root_;
   delete memory_root_;
-  delete owned_tagged_memory_;
   delete pc_src_operand_;
   for (auto *csr : csr_vec_) delete csr;
   delete csr_set_;
diff --git a/cheriot/cheriot_state.h b/cheriot/cheriot_state.h
index 7df9bba..c14c73e 100644
--- a/cheriot/cheriot_state.h
+++ b/cheriot/cheriot_state.h
@@ -168,8 +168,6 @@
                util::AtomicMemoryOpInterface *atomic_memory);
   CheriotState(std::string_view id, util::TaggedMemoryInterface *memory)
       : CheriotState(id, memory, nullptr) {}
-  explicit CheriotState(std::string_view id)
-      : CheriotState(id, nullptr, nullptr) {}
   ~CheriotState() override;
 
   // Deleted constructors and operators.
@@ -283,7 +281,7 @@
   inline void reset_is_interrupt_available() {
     is_interrupt_available_ = false;
   }
-  void set_memory(util::TaggedMemoryInterface *tagged_memory) {
+  void set_tagged_memory(util::TaggedMemoryInterface *tagged_memory) {
     tagged_memory_ = tagged_memory;
   }
   util::TaggedMemoryInterface *tagged_memory() const { return tagged_memory_; }
@@ -387,7 +385,6 @@
   uint64_t max_physical_address_;
   uint64_t min_physical_address_ = 0;
   int num_tags_per_load_;
-  util::TaggedMemoryInterface *owned_tagged_memory_ = nullptr;
   util::TaggedMemoryInterface *tagged_memory_;
   util::AtomicMemoryOpInterface *atomic_tagged_memory_;
   RiscVCsrSet *csr_set_;
diff --git a/cheriot/cheriot_top.cc b/cheriot/cheriot_top.cc
index a8b7221..5321f0d 100644
--- a/cheriot/cheriot_top.cc
+++ b/cheriot/cheriot_top.cc
@@ -38,6 +38,7 @@
 #include "cheriot/riscv_cheriot_register_aliases.h"
 #include "mpact/sim/generic/component.h"
 #include "mpact/sim/generic/core_debug_interface.h"
+#include "mpact/sim/generic/counters.h"
 #include "mpact/sim/generic/data_buffer.h"
 #include "mpact/sim/generic/decode_cache.h"
 #include "mpact/sim/generic/type_helpers.h"
@@ -62,71 +63,11 @@
 using EC = ::mpact::sim::cheriot::ExceptionCode;
 using PB = ::mpact::sim::cheriot::CheriotRegister::PermissionBits;
 
-CheriotTop::CheriotTop(std::string name)
+CheriotTop::CheriotTop(std::string name, CheriotState *state,
+                       CheriotDecoder *decoder)
     : Component(name),
-      owns_memory_(true),
-      counter_num_instructions_("num_instructions", 0),
-      counter_num_cycles_("num_cycles", 0),
-      counter_pc_("pc", 0),
-      cap_reg_re_{
-          R"((\w+)\.(top|base|length|tag|permissions|object_type|reserved))"} {
-  data_memory_ = new util::TaggedFlatDemandMemory(8);
-  inst_memory_ = data_memory_;
-  Initialize();
-}
-
-CheriotTop::CheriotTop(std::string name, util::TaggedMemoryInterface *memory)
-    : Component(name),
-      inst_memory_(memory),
-      data_memory_(memory),
-      atomic_memory_(nullptr),
-      owns_memory_(false),
-      counter_num_instructions_("num_instructions", 0),
-      counter_num_cycles_("num_cycles", 0),
-      counter_pc_("pc", 0),
-      cap_reg_re_{
-          R"((\w+)\.(top|base|length|tag|permissions|object_type|reserved))"} {
-  Initialize();
-}
-
-CheriotTop::CheriotTop(std::string name, util::MemoryInterface *inst_memory,
-                       util::TaggedMemoryInterface *data_memory)
-    : Component(name),
-      inst_memory_(inst_memory),
-      data_memory_(data_memory),
-      atomic_memory_(nullptr),
-      owns_memory_(false),
-      counter_num_instructions_("num_instructions", 0),
-      counter_num_cycles_("num_cycles", 0),
-      counter_pc_("pc", 0),
-      cap_reg_re_{
-          R"((\w+)\.(top|base|length|tag|permissions|object_type|reserved))"} {
-  Initialize();
-}
-
-CheriotTop::CheriotTop(std::string name, util::TaggedMemoryInterface *memory,
-                       util::MemoryInterface *atomic_memory_if)
-    : Component(name),
-      inst_memory_(memory),
-      data_memory_(memory),
-      atomic_memory_if_(atomic_memory_if),
-      owns_memory_(false),
-      counter_num_instructions_("num_instructions", 0),
-      counter_num_cycles_("num_cycles", 0),
-      counter_pc_("pc", 0),
-      cap_reg_re_{
-          R"((\w+)\.(top|base|length|tag|permissions|object_type|reserved))"} {
-  Initialize();
-}
-
-CheriotTop::CheriotTop(std::string name, util::MemoryInterface *inst_memory,
-                       util::TaggedMemoryInterface *data_memory,
-                       util::MemoryInterface *atomic_memory_if)
-    : Component(name),
-      inst_memory_(inst_memory),
-      data_memory_(data_memory),
-      atomic_memory_if_(atomic_memory_if),
-      owns_memory_(false),
+      state_(state),
+      cheriot_decoder_(decoder),
       counter_num_instructions_("num_instructions", 0),
       counter_num_cycles_("num_cycles", 0),
       counter_pc_("pc", 0),
@@ -148,32 +89,35 @@
 
   delete rv_bp_manager_;
   delete cheriot_decode_cache_;
-  delete cheriot_decoder_;
-  delete state_;
-  delete tagged_watcher_;
-  delete atomic_watcher_;
   delete atomic_memory_;
-  if (owns_memory_) delete inst_memory_;
+  delete tagged_watcher_;
+  delete memory_watcher_;
 }
 
 void CheriotTop::Initialize() {
-  // Create the simulation state.
-  tagged_watcher_ = new util::TaggedMemoryWatcher(data_memory_);
-  atomic_watcher_ = new util::MemoryWatcher(atomic_memory_if_);
-  atomic_memory_ = new util::AtomicMemory(atomic_watcher_);
-  state_ = new CheriotState(kCheriotName, tagged_watcher_, atomic_memory_);
+  // Create the watchers.
+  auto *memory = static_cast<util::MemoryInterface *>(state_->tagged_memory());
+  tagged_watcher_ = new util::TaggedMemoryWatcher(state_->tagged_memory());
+  memory_watcher_ = new util::MemoryWatcher(memory);
+
+  atomic_memory_ = new util::AtomicMemory(memory_watcher_);
+  state_->set_tagged_memory(tagged_watcher_);
+  state_->set_atomic_tagged_memory(atomic_memory_);
+
   pcc_ = static_cast<CheriotRegister *>(
       state_->registers()->at(CheriotState::kPcName));
-  // Set up the decoder and decode cache.
-  cheriot_decoder_ = new CheriotDecoder(state_, inst_memory_);
-  // Register instruction opcode counters.
-  for (int i = 0; i < static_cast<int>(isa32::OpcodeEnum::kPastMaxValue); i++) {
-    counter_opcode_[i].Initialize(absl::StrCat("num_", isa32::kOpcodeNames[i]),
-                                  0);
+
+  // Register opcode counters.
+  int num_opcodes = cheriot_decoder_->GetNumOpcodes();
+  counter_opcode_.resize(num_opcodes);
+  for (int i = 0; i < num_opcodes; i++) {
+    counter_opcode_.push_back(generic::SimpleCounter<uint64_t>());
+    counter_opcode_[i].Initialize(
+        absl::StrCat("num_", cheriot_decoder_->GetOpcodeName(i)), 0);
     CHECK_OK(AddCounter(&counter_opcode_[i]))
-        << absl::StrCat("Failed to register opcode counter for :'",
-                        isa32::kOpcodeNames[i], "'");
+        << "Failed to register opcode counter";
   }
+
   cheriot_decode_cache_ =
       generic::DecodeCache::Create({16 * 1024, 2}, cheriot_decoder_);
   // Register instruction counter.
@@ -184,8 +128,8 @@
 
   // Breakpoints.
   rv_action_point_manager_ = new riscv::RiscVActionPointManager(
-      inst_memory_, absl::bind_front(&generic::DecodeCache::Invalidate,
-                                     cheriot_decode_cache_));
+      memory, absl::bind_front(&generic::DecodeCache::Invalidate,
+                               cheriot_decode_cache_));
   rv_bp_manager_ = new riscv::RiscVBreakpointManager(
       rv_action_point_manager_,
       [this](HaltReason halt_reason) { RequestHalt(halt_reason, nullptr); });
@@ -231,21 +175,25 @@
 }
 
 bool CheriotTop::ExecuteInstruction(Instruction *inst) {
+  // Check that pcc has tag set.
   if (!pcc_->tag()) {
     state_->HandleCheriRegException(inst, inst->address(),
                                     EC::kCapExTagViolation, pcc_);
     return true;
   }
+  // Check that pcc has execute permission.
   if (!pcc_->HasPermission(PB::kPermitExecute)) {
     state_->HandleCheriRegException(inst, inst->address(),
                                     EC::kCapExPermitExecuteViolation, pcc_);
     return true;
   }
+  // Check that pcc is within bounds.
   if (!pcc_->IsInBounds(inst->address(), inst->size())) {
     state_->HandleCheriRegException(inst, inst->address(),
                                     EC::kCapExBoundsViolation, pcc_);
     return true;
   }
+  // Execute the instruction.
   inst->Execute(nullptr);
   counter_pc_.SetValue(inst->address());
   // Comment out instruction logging during execution.
@@ -859,7 +807,7 @@
         });
     if (!rd_tagged_status.ok()) return rd_tagged_status;
 
-    auto rd_atomic_status = atomic_watcher_->SetLoadWatchCallback(
+    auto rd_atomic_status = memory_watcher_->SetLoadWatchCallback(
         util::MemoryWatcher::AddressRange(address, address + length - 1),
         [this](uint64_t address, int size) {
           set_halt_string(absl::StrFormat(
@@ -885,12 +833,12 @@
       if (access_type == AccessType::kLoadStore) {
         // Error recovery - ignore return value.
         (void)tagged_watcher_->ClearLoadWatchCallback(address);
-        (void)atomic_watcher_->ClearLoadWatchCallback(address);
+        (void)memory_watcher_->ClearLoadWatchCallback(address);
       }
       return wr_tagged_status;
     }
 
-    auto wr_atomic_status = atomic_watcher_->SetStoreWatchCallback(
+    auto wr_atomic_status = memory_watcher_->SetStoreWatchCallback(
         util::MemoryWatcher::AddressRange(address, address + length - 1),
         [this](uint64_t address, int size) {
           set_halt_string(absl::StrFormat(
@@ -902,7 +850,7 @@
       (void)tagged_watcher_->ClearStoreWatchCallback(address);
       if (access_type == AccessType::kLoadStore) {
         (void)tagged_watcher_->ClearLoadWatchCallback(address);
-        (void)atomic_watcher_->ClearLoadWatchCallback(address);
+        (void)memory_watcher_->ClearLoadWatchCallback(address);
       }
       return wr_atomic_status;
     }
@@ -917,7 +865,7 @@
     auto rd_tagged_status = tagged_watcher_->ClearLoadWatchCallback(address);
     if (!rd_tagged_status.ok()) return rd_tagged_status;
 
-    auto rd_atomic_status = atomic_watcher_->ClearLoadWatchCallback(address);
+    auto rd_atomic_status = memory_watcher_->ClearLoadWatchCallback(address);
     if (!rd_atomic_status.ok()) return rd_atomic_status;
   }
   if ((access_type == AccessType::kStore) ||
@@ -925,7 +873,7 @@
     auto wr_tagged_status = tagged_watcher_->ClearStoreWatchCallback(address);
     if (!wr_tagged_status.ok()) return wr_tagged_status;
 
-    auto wr_atomic_status = atomic_watcher_->ClearStoreWatchCallback(address);
+    auto wr_atomic_status = memory_watcher_->ClearStoreWatchCallback(address);
     if (!wr_atomic_status.ok()) return wr_atomic_status;
   }
   return absl::OkStatus();
diff --git a/cheriot/cheriot_top.h b/cheriot/cheriot_top.h
index 98d0421..4fc8d17 100644
--- a/cheriot/cheriot_top.h
+++ b/cheriot/cheriot_top.h
@@ -20,6 +20,7 @@
 #include <cstddef>
 #include <cstdint>
 #include <string>
+#include <vector>
 
 #include "absl/container/flat_hash_map.h"
 #include "absl/functional/any_invocable.h"
@@ -27,6 +28,7 @@
 #include "absl/status/statusor.h"
 #include "absl/synchronization/notification.h"
 #include "cheriot/cheriot_debug_interface.h"
+#include "cheriot/cheriot_decoder.h"
 #include "cheriot/cheriot_register.h"
 #include "cheriot/cheriot_state.h"
 #include "cheriot/riscv_cheriot_enums.h"
@@ -63,22 +65,7 @@
   using RunStatus = generic::CoreDebugInterface::RunStatus;
   using HaltReason = generic::CoreDebugInterface::HaltReason;
 
-  // Simple constructor, the memories are created and owned by the CheriotTop
-  // instance.
-  explicit CheriotTop(std::string name);
-  // Constructors without the atomic memory interface. Either one (unified),
-  // or two (inst and data) interfaces are passed in.
-  CheriotTop(std::string name, util::TaggedMemoryInterface *memory);
-  CheriotTop(std::string name, util::MemoryInterface *inst_memory,
-             util::TaggedMemoryInterface *data_memory);
-  // Constructors with the memory interface to be used with atomic memory
-  // operations. Either one (unified), or two (inst and data) interfaces are
-  // passed in.
-  CheriotTop(std::string name, util::TaggedMemoryInterface *memory,
-             util::MemoryInterface *atomic_memory_if);
-  CheriotTop(std::string name, util::MemoryInterface *inst_memory,
-             util::TaggedMemoryInterface *data_memory,
-             util::MemoryInterface *atomic_memory_if);
+  CheriotTop(std::string name, CheriotState *state, CheriotDecoder *decoder);
   ~CheriotTop() override;
 
   // Methods inherited from CoreDebugInterface.
@@ -138,8 +125,6 @@
 
   // Accessors.
   CheriotState *state() const { return state_; }
-  util::TaggedMemoryInterface *data_memory() const { return data_memory_; }
-  util::MemoryInterface *inst_memory() const { return inst_memory_; }
   // The following are not const as callers may need to call non-const methods
   // of the counter.
   generic::SimpleCounter<uint64_t> *counter_num_instructions() {
@@ -151,7 +136,7 @@
   generic::SimpleCounter<uint64_t> *counter_pc() { return &counter_pc_; }
   // Memory watchers used for data watch points.
   util::TaggedMemoryWatcher *tagged_watcher() { return tagged_watcher_; }
-  util::MemoryWatcher *atomic_watcher() { return atomic_watcher_; }
+  util::MemoryWatcher *memory_watcher() { return memory_watcher_; }
 
   const std::string &halt_string() const { return halt_string_; }
   void set_halt_string(std::string halt_string) { halt_string_ = halt_string; }
@@ -192,13 +177,9 @@
   generic::DecoderInterface *cheriot_decoder_ = nullptr;
   // Decode cache, memory and memory watcher.
   generic::DecodeCache *cheriot_decode_cache_ = nullptr;
-  util::MemoryInterface *inst_memory_ = nullptr;
-  util::TaggedMemoryInterface *data_memory_ = nullptr;
-  util::MemoryInterface *atomic_memory_if_ = nullptr;
   util::AtomicMemoryOpInterface *atomic_memory_ = nullptr;
-  bool owns_memory_ = false;
   util::TaggedMemoryWatcher *tagged_watcher_ = nullptr;
-  util::MemoryWatcher *atomic_watcher_ = nullptr;
+  util::MemoryWatcher *memory_watcher_ = nullptr;
   // Branch trace info - uses a circular buffer. The size is defined by the
   // constant kBranchTraceSize in the .cc file.
   BranchTraceEntry *branch_trace_;
@@ -211,8 +192,7 @@
   int branch_trace_mask_ = kBranchTraceSize - 1;
   int branch_trace_size_ = kBranchTraceSize;
   // Counter for the number of instructions simulated.
-  generic::SimpleCounter<uint64_t>
-      counter_opcode_[*isa32::OpcodeEnum::kPastMaxValue];
+  std::vector<generic::SimpleCounter<uint64_t>> counter_opcode_;
   generic::SimpleCounter<uint64_t> counter_num_instructions_;
   generic::SimpleCounter<uint64_t> counter_num_cycles_;
   // Counter used for profiling by connecting it to a profiler. This allows
diff --git a/cheriot/mpact_cheriot.cc b/cheriot/mpact_cheriot.cc
index 65dce13..9174c63 100644
--- a/cheriot/mpact_cheriot.cc
+++ b/cheriot/mpact_cheriot.cc
@@ -39,6 +39,7 @@
 #include "absl/strings/string_view.h"
 #include "absl/time/clock.h"
 #include "absl/time/time.h"
+#include "cheriot/cheriot_decoder.h"
 #include "cheriot/cheriot_instrumentation_control.h"
 #include "cheriot/cheriot_top.h"
 #include "cheriot/debug_command_shell.h"
@@ -63,9 +64,11 @@
 #include "riscv//riscv_clint.h"
 #include "src/google/protobuf/text_format.h"
 
-using ::mpact::sim::proto::ComponentData;
 using AddressRange = mpact::sim::util::MemoryWatcher::AddressRange;
+using ::mpact::sim::cheriot::CheriotDecoder;
 using ::mpact::sim::cheriot::CheriotInstrumentationControl;
+using ::mpact::sim::cheriot::CheriotState;
+using ::mpact::sim::proto::ComponentData;
 using ::mpact::sim::util::InstructionProfiler;
 using ::mpact::sim::util::TaggedMemoryUseProfiler;
 
@@ -251,8 +254,12 @@
     memory_use_profiler->set_is_enabled(false);
     data_memory = memory_use_profiler;
   }
-  CheriotTop cheriot_top("Cheriot", static_cast<MemoryInterface *>(router),
-                         data_memory, static_cast<MemoryInterface *>(router));
+  CheriotState cheriot_state("CherIoT", data_memory,
+                             static_cast<AtomicMemoryOpInterface *>(router));
+  CheriotDecoder cheriot_decoder(&cheriot_state,
+                                 static_cast<MemoryInterface *>(router));
+
+  CheriotTop cheriot_top("Cheriot", &cheriot_state, &cheriot_decoder);
 
   // Enable instruction profiling if the flag is set.
   InstructionProfiler *inst_profiler = nullptr;
@@ -343,9 +350,10 @@
   }
 
   // Set up semihosting.
-  auto *semihost = new RiscVArmSemihost(RiscVArmSemihost::BitWidth::kWord32,
-                                        cheriot_top.inst_memory(),
-                                        cheriot_top.data_memory());
+  auto *memory = static_cast<MemoryInterface *>(router);
+  auto *semihost =
+      new RiscVArmSemihost(RiscVArmSemihost::BitWidth::kWord32, memory, memory);
+
   cheriot_top.state()->AddEbreakHandler([semihost](const Instruction *inst) {
     if (semihost->IsSemihostingCall(inst)) {
       semihost->OnEBreak(inst);
diff --git a/cheriot/test/BUILD b/cheriot/test/BUILD
index bcac027..c4920c9 100644
--- a/cheriot/test/BUILD
+++ b/cheriot/test/BUILD
@@ -16,27 +16,6 @@
 
 package(default_applicable_licenses = ["//:license"])
 
-cc_library(
-    name = "riscv_cheriot_fp_test_base",
-    testonly = True,
-    hdrs = ["riscv_cheriot_fp_test_base.h"],
-    copts = [
-        "-ffp-model=strict",
-        "-fprotect-parens",
-    ],
-    tags = ["not_run:arm"],
-    deps = [
-        "//cheriot:riscv_cheriot",
-        "@com_google_absl//absl/random",
-        "@com_google_absl//absl/strings",
-        "@com_google_absl//absl/types:span",
-        "@com_google_googletest//:gtest_for_library_testonly",
-        "@com_google_mpact-riscv//riscv:riscv_state",
-        "@com_google_mpact-sim//mpact/sim/generic:instruction",
-        "@com_google_mpact-sim//mpact/sim/util/memory",
-    ],
-)
-
 cc_test(
     name = "cheriot_register_test",
     size = "small",
@@ -102,6 +81,7 @@
         "@com_google_mpact-sim//mpact/sim/generic:core",
         "@com_google_mpact-sim//mpact/sim/generic:instruction",
         "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+        "@com_google_mpact-sim//mpact/sim/util/memory",
     ],
 )
 
@@ -118,6 +98,7 @@
         "@com_google_mpact-sim//mpact/sim/generic:core",
         "@com_google_mpact-sim//mpact/sim/generic:instruction",
         "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+        "@com_google_mpact-sim//mpact/sim/util/memory",
     ],
 )
 
@@ -137,6 +118,7 @@
         "@com_google_mpact-riscv//riscv:riscv_state",
         "@com_google_mpact-sim//mpact/sim/generic:core",
         "@com_google_mpact-sim//mpact/sim/generic:instruction",
+        "@com_google_mpact-sim//mpact/sim/util/memory",
     ],
 )
 
@@ -152,6 +134,7 @@
         "//cheriot:riscv_cheriot_isa",
         "@com_google_googletest//:gtest_main",
         "@com_google_mpact-sim//mpact/sim/generic:type_helpers",
+        "@com_google_mpact-sim//mpact/sim/util/memory",
     ],
 )
 
diff --git a/cheriot/test/cheriot_state_test.cc b/cheriot/test/cheriot_state_test.cc
index 28c0c04..85c728e 100644
--- a/cheriot/test/cheriot_state_test.cc
+++ b/cheriot/test/cheriot_state_test.cc
@@ -30,6 +30,7 @@
 
 using ::mpact::sim::cheriot::CheriotRegister;
 using ::mpact::sim::cheriot::CheriotState;
+using ::mpact::sim::util::TaggedFlatDemandMemory;
 using RVEC = ::mpact::sim::riscv::ExceptionCode;
 
 constexpr int kPcValue = 0x1000;
@@ -40,7 +41,9 @@
 // additional functionality over the ArchState class.
 
 TEST(CheriotStateTest, Basic) {
-  auto *state = new CheriotState("test");
+  TaggedFlatDemandMemory mem(8);
+
+  auto *state = new CheriotState("test", &mem, nullptr);
   // Make sure pc has been created.
   auto iter = state->registers()->find("pcc");
   auto *ptr = (iter != state->registers()->end()) ? iter->second : nullptr;
@@ -56,7 +59,9 @@
 }
 
 TEST(CheriotStateTest, Memory) {
-  auto *state = new CheriotState("test");
+  TaggedFlatDemandMemory mem(8);
+
+  auto *state = new CheriotState("test", &mem, nullptr);
   auto *db = state->db_factory()->Allocate<uint32_t>(1);
   state->LoadMemory(nullptr, kMemAddr, db, nullptr, nullptr);
   EXPECT_EQ(db->Get<uint32_t>(0), 0);
@@ -70,7 +75,9 @@
 }
 
 TEST(CheriotStateTest, OutOfBoundLoad) {
-  auto *state = new CheriotState("test");
+  TaggedFlatDemandMemory mem(8);
+
+  auto *state = new CheriotState("test", &mem, nullptr);
   state->set_max_physical_address(kMemAddr - 4);
   state->set_on_trap([](bool is_interrupt, uint64_t trap_value,
                         uint64_t exception_code, uint64_t epc,
diff --git a/cheriot/test/riscv_cheriot_encoding_test.cc b/cheriot/test/riscv_cheriot_encoding_test.cc
index 2f941e7..31604ba 100644
--- a/cheriot/test/riscv_cheriot_encoding_test.cc
+++ b/cheriot/test/riscv_cheriot_encoding_test.cc
@@ -20,15 +20,17 @@
 #include "cheriot/riscv_cheriot_enums.h"
 #include "googlemock/include/gmock/gmock.h"
 #include "mpact/sim/generic/type_helpers.h"
+#include "mpact/sim/util/memory/tagged_flat_demand_memory.h"
 
 namespace {
 
 using ::mpact::sim::cheriot::CheriotState;
 using ::mpact::sim::cheriot::isa32::kOpcodeNames;
 using ::mpact::sim::cheriot::isa32::RiscVCheriotEncoding;
+using ::mpact::sim::generic::operator*;  // NOLINT: is used below (clang error).
+using ::mpact::sim::util::TaggedFlatDemandMemory;
 using SlotEnum = mpact::sim::cheriot::isa32::SlotEnum;
 using OpcodeEnum = mpact::sim::cheriot::isa32::OpcodeEnum;
-using ::mpact::sim::generic::operator*;  // NOLINT: is used below (clang error).
 
 // Constexpr for opcodes for RV32 CHERIoT instructions grouped by isa group.
 
@@ -186,14 +188,17 @@
 class RiscVCheriotEncodingTest : public testing::Test {
  protected:
   RiscVCheriotEncodingTest() {
-    state_ = new CheriotState("test");
+    mem_ = new TaggedFlatDemandMemory(8);
+    state_ = new CheriotState("test", mem_, nullptr);
     enc_ = new RiscVCheriotEncoding(state_);
   }
   ~RiscVCheriotEncodingTest() override {
     delete enc_;
+    delete mem_;
     delete state_;
   }
 
+  TaggedFlatDemandMemory *mem_;
   CheriotState *state_;
   RiscVCheriotEncoding *enc_;
 };
diff --git a/cheriot/test/riscv_cheriot_fp_test_base.h b/cheriot/test/riscv_cheriot_fp_test_base.h
deleted file mode 100644
index b47923d..0000000
--- a/cheriot/test/riscv_cheriot_fp_test_base.h
+++ /dev/null
@@ -1,781 +0,0 @@
-/*
- * Copyright 2024 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     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 MPACT_CHERIOT___TEST_RISCV_CHERIOT_FP_TEST_BASE_H_
-#define MPACT_CHERIOT___TEST_RISCV_CHERIOT_FP_TEST_BASE_H_
-
-#include <cmath>
-#include <cstdint>
-#include <functional>
-#include <ios>
-#include <limits>
-#include <string>
-#include <tuple>
-#include <type_traits>
-#include <vector>
-
-#include "absl/random/random.h"
-#include "absl/strings/str_cat.h"
-#include "absl/strings/string_view.h"
-#include "absl/types/span.h"
-#include "cheriot/cheriot_register.h"
-#include "cheriot/cheriot_state.h"
-#include "cheriot/riscv_cheriot_fp_state.h"
-#include "googlemock/include/gmock/gmock.h"
-#include "mpact/sim/generic/instruction.h"
-#include "mpact/sim/util/memory/tagged_flat_demand_memory.h"
-#include "riscv//riscv_fp_host.h"
-#include "riscv//riscv_fp_info.h"
-#include "riscv//riscv_register.h"
-
-namespace mpact {
-namespace sim {
-namespace cheriot {
-namespace test {
-
-using ::mpact::sim::cheriot::CheriotRegister;
-using ::mpact::sim::cheriot::CheriotState;
-using ::mpact::sim::cheriot::RiscVCheriotFPState;
-using ::mpact::sim::generic::Instruction;
-using ::mpact::sim::riscv::FPRoundingMode;
-using ::mpact::sim::riscv::RV64Register;
-using ::mpact::sim::riscv::RVFpRegister;
-using ::mpact::sim::riscv::ScopedFPRoundingMode;
-using ::mpact::sim::riscv::ScopedFPStatus;
-using ::mpact::sim::util::TaggedFlatDemandMemory;
-
-constexpr int kTestValueLength = 256;
-
-constexpr uint32_t kInstAddress = 0x1000;
-constexpr uint32_t kDataLoadAddress = 0x1'0000;
-constexpr uint32_t kDataStoreAddress = 0x2'0000;
-
-// Templated helper structs to provide information about floating point types.
-template <typename T>
-struct FPTypeInfo {
-  using IntType = T;
-  static const int kBitSize = 8 * sizeof(T);
-  static const int kExpSize = 0;
-  static const int kSigSize = 0;
-  static bool IsNaN(T value) { return false; }
-  static constexpr IntType kQNaN = 0;
-  static constexpr IntType kSNaN = 0;
-  static constexpr IntType kPosInf = std::numeric_limits<T>::max();
-  static constexpr IntType kNegInf = std::numeric_limits<T>::min();
-  static constexpr IntType kPosZero = 0;
-  static constexpr IntType kNegZero = 0;
-  static constexpr IntType kPosDenorm = 0;
-  static constexpr IntType kNegDenorm = 0;
-};
-
-template <>
-struct FPTypeInfo<float> {
-  using T = float;
-  using IntType = uint32_t;
-  static const int kExpBias = 127;
-  static const int kBitSize = sizeof(float) << 3;
-  static const int kExpSize = 8;
-  static const int kSigSize = kBitSize - kExpSize - 1;
-  static const IntType kExpMask = ((1ULL << kExpSize) - 1) << kSigSize;
-  static const IntType kSigMask = (1ULL << kSigSize) - 1;
-  static const IntType kQNaN = kExpMask | (1ULL << (kSigSize - 1)) | 1;
-  static const IntType kSNaN = kExpMask | 1;
-  static const IntType kPosInf = kExpMask;
-  static const IntType kNegInf = kExpMask | (1ULL << (kBitSize - 1));
-  static const IntType kPosZero = 0;
-  static const IntType kNegZero = 1ULL << (kBitSize - 1);
-  static const IntType kPosDenorm = 1ULL << (kSigSize - 2);
-  static const IntType kNegDenorm =
-      (1ULL << (kBitSize - 1)) | (1ULL << (kSigSize - 2));
-  static const IntType kCanonicalNaN = 0x7fc0'0000ULL;
-  static bool IsNaN(T value) { return std::isnan(value); }
-};
-
-template <>
-struct FPTypeInfo<double> {
-  using T = double;
-  using IntType = uint64_t;
-  static const int kExpBias = 1023;
-  static const int kBitSize = sizeof(double) << 3;
-  static const int kExpSize = 11;
-  static const int kSigSize = kBitSize - kExpSize - 1;
-  static const IntType kExpMask = ((1ULL << kExpSize) - 1) << kSigSize;
-  static const IntType kSigMask = (1ULL << kSigSize) - 1;
-  static const IntType kQNaN = kExpMask | (1ULL << (kSigSize - 1)) | 1;
-  static const IntType kSNaN = kExpMask | 1;
-  static const IntType kPosInf = kExpMask;
-  static const IntType kNegInf = kExpMask | (1ULL << (kBitSize - 1));
-  static const IntType kPosZero = 0;
-  static const IntType kNegZero = 1ULL << (kBitSize - 1);
-  static const IntType kPosDenorm = 1ULL << (kSigSize - 2);
-  static const IntType kNegDenorm =
-      (1ULL << (kBitSize - 1)) | (1ULL << (kSigSize - 2));
-  static const IntType kCanonicalNaN = 0x7ff8'0000'0000'0000ULL;
-  static bool IsNaN(T value) { return std::isnan(value); }
-};
-
-// Templated helper function for classifying fp numbers.
-template <typename T>
-typename FPTypeInfo<T>::IntType VfclassVHelper(T val) {
-  auto fp_class = fpclassify(val);
-  switch (fp_class) {
-    case FP_INFINITE:
-      return std::signbit(val) ? 1 : 1 << 7;
-    case FP_NAN: {
-      auto uint_val =
-          *reinterpret_cast<typename FPTypeInfo<T>::IntType *>(&val);
-      bool quiet_nan = (uint_val >> (FPTypeInfo<T>::kSigSize - 1)) & 1;
-      return quiet_nan ? 1 << 9 : 1 << 8;
-    }
-    case FP_ZERO:
-      return std::signbit(val) ? 1 << 3 : 1 << 4;
-    case FP_SUBNORMAL:
-      return std::signbit(val) ? 1 << 2 : 1 << 5;
-    case FP_NORMAL:
-      return std::signbit(val) ? 1 << 1 : 1 << 6;
-  }
-  return 0;
-}
-
-// These templated functions allow for comparison of values with a tolerance
-// given for floating point types. The tolerance is stated as the bit position
-// in the mantissa of the op, with 0 being the msb of the mantissa. If the
-// bit position is beyond the mantissa, a comparison of equal is performed.
-template <typename T>
-inline void FPCompare(T op, T reg, int, absl::string_view str) {
-  EXPECT_EQ(reg, op) << str;
-}
-
-template <>
-inline void FPCompare<float>(float op, float reg, int delta_position,
-                             absl::string_view str) {
-  using T = float;
-  using UInt = typename FPTypeInfo<T>::IntType;
-  UInt u_op = *reinterpret_cast<UInt *>(&op);
-  UInt u_reg = *reinterpret_cast<UInt *>(&reg);
-  if (!std::isnan(op) && !std::isinf(op) &&
-      delta_position < FPTypeInfo<T>::kSigSize) {
-    T delta;
-    UInt exp = FPTypeInfo<T>::kExpMask >> FPTypeInfo<T>::kSigSize;
-    if (exp > delta_position) {
-      exp -= delta_position;
-      UInt udelta = exp << FPTypeInfo<T>::kSigSize;
-      delta = *reinterpret_cast<T *>(&udelta);
-    } else {
-      // Becomes a denormal
-      int diff = delta_position - exp;
-      UInt udelta = 1ULL << (FPTypeInfo<T>::kSigSize - 1 - diff);
-      delta = *reinterpret_cast<T *>(&udelta);
-    }
-    EXPECT_THAT(reg, testing::NanSensitiveFloatNear(op, delta))
-        << str << "  op: " << std::hex << u_op << "  reg: " << std::hex
-        << u_reg;
-  } else {
-    EXPECT_THAT(reg, testing::NanSensitiveFloatEq(op))
-        << str << "  op: " << std::hex << u_op << "  reg: " << std::hex
-        << u_reg;
-  }
-}
-
-template <>
-inline void FPCompare<double>(double op, double reg, int delta_position,
-                              absl::string_view str) {
-  using T = double;
-  using UInt = typename FPTypeInfo<T>::IntType;
-  UInt u_op = *reinterpret_cast<UInt *>(&op);
-  UInt u_reg = *reinterpret_cast<UInt *>(&reg);
-  if (!std::isnan(op) && !std::isinf(op) &&
-      delta_position < FPTypeInfo<T>::kSigSize) {
-    T delta;
-    UInt exp = FPTypeInfo<T>::kExpMask >> FPTypeInfo<T>::kSigSize;
-    if (exp > delta_position) {
-      exp -= delta_position;
-      UInt udelta = exp << FPTypeInfo<T>::kSigSize;
-      delta = *reinterpret_cast<T *>(&udelta);
-    } else {
-      // Becomes a denormal
-      int diff = delta_position - exp;
-      UInt udelta = 1ULL << (FPTypeInfo<T>::kSigSize - 1 - diff);
-      delta = *reinterpret_cast<T *>(&udelta);
-    }
-    EXPECT_THAT(reg, testing::NanSensitiveDoubleNear(op, delta))
-        << str << "  op: " << std::hex << u_op << "  reg: " << std::hex
-        << u_reg;
-  } else {
-    EXPECT_THAT(reg, testing::NanSensitiveDoubleEq(op))
-        << str << "  op: " << std::hex << u_op << "  reg: " << std::hex
-        << u_reg;
-  }
-}
-
-template <typename FP>
-FP OptimizationBarrier(FP op) {
-  asm volatile("" : "+X"(op));
-  return op;
-}
-
-namespace internal {
-
-// These are predicates used in the following NaNBox function definitions, as
-// part of the enable_if construct.
-template <typename S, typename D>
-struct EqualSize {
-  static const bool value = sizeof(S) == sizeof(D) &&
-                            std::is_floating_point<S>::value &&
-                            std::is_integral<D>::value;
-};
-
-template <typename S, typename D>
-struct GreaterSize {
-  static const bool value =
-      sizeof(S) > sizeof(D) &&
-      std::is_floating_point<S>::value &&std::is_integral<D>::value;
-};
-
-template <typename S, typename D>
-struct LessSize {
-  static const bool value = sizeof(S) < sizeof(D) &&
-                            std::is_floating_point<S>::value &&
-                            std::is_integral<D>::value;
-};
-
-}  // namespace internal
-
-// Template functions to NaN box a floating point value when being assigned
-// to a wider register. The first version places a smaller floating point value
-// in a NaN box (all upper bits in the word are set to 1).
-
-// Enable_if is used to select the proper implementation for different S and D
-// type combinations. It uses the SFINAE (substitution failure is not an error)
-// "feature" of C++ to hide the implementation that don't match the predicate
-// from being resolved.
-
-template <typename S, typename D>
-inline typename std::enable_if<internal::LessSize<S, D>::value, D>::type NaNBox(
-    S value) {
-  using SInt = typename FPTypeInfo<S>::IntType;
-  SInt sval = *reinterpret_cast<SInt *>(&value);
-  D dval = (~static_cast<D>(0) << (sizeof(S) * 8)) | sval;
-  return *reinterpret_cast<D *>(&dval);
-}
-
-// This version does a straight copy - as the data types are the same size.
-template <typename S, typename D>
-inline typename std::enable_if<internal::EqualSize<S, D>::value, D>::type
-NaNBox(S value) {
-  return *reinterpret_cast<D *>(&value);
-}
-
-// Signal error if the register is smaller than the floating point value.
-template <typename S, typename D>
-inline typename std::enable_if<internal::GreaterSize<S, D>::value, D>::type
-NaNBox(S value) {
-  // No return statement, so error will be reported.
-}
-
-class RiscVFPInstructionTestBase : public testing::Test {
- public:
-  RiscVFPInstructionTestBase() {
-    memory_ = new TaggedFlatDemandMemory(8);
-    state_ = new CheriotState("test", memory_);
-    rv_fp_ = new RiscVCheriotFPState(state_);
-    state_->set_rv_fp(rv_fp_);
-    instruction_ = new Instruction(kInstAddress, state_);
-    instruction_->set_size(4);
-    child_instruction_ = new Instruction(kInstAddress, state_);
-    child_instruction_->set_size(4);
-    // Initialize a portion of memory with a known pattern.
-    auto *db = state_->db_factory()->Allocate(8192);
-    auto span = db->Get<uint8_t>();
-    for (int i = 0; i < 8192; i++) {
-      span[i] = i & 0xff;
-    }
-    memory_->Store(kDataLoadAddress - 4096, db);
-    db->DecRef();
-    for (int i = 1; i < 32; i++) {
-      creg_[i] =
-          state_->GetRegister<CheriotRegister>(absl::StrCat("c", i)).first;
-    }
-    for (int i = 1; i < 32; i++) {
-      freg_[i] = state_->GetRegister<RVFpRegister>(absl::StrCat("f", i)).first;
-    }
-    for (int i = 1; i < 32; i++) {
-      dreg_[i] = state_->GetRegister<RVFpRegister>(absl::StrCat("d", i)).first;
-    }
-  }
-  ~RiscVFPInstructionTestBase() override {
-    delete rv_fp_;
-    rv_fp_ = nullptr;
-    state_->set_rv_fp(nullptr);
-    delete state_;
-    state_ = nullptr;
-    if (instruction_ != nullptr) instruction_->DecRef();
-    instruction_ = nullptr;
-    if (child_instruction_ != nullptr) child_instruction_->DecRef();
-    child_instruction_ = nullptr;
-    delete memory_;
-    memory_ = nullptr;
-  }
-
-  // Clear the instruction instance and allocate a new one.
-  void ResetInstruction() {
-    instruction_->DecRef();
-    instruction_ = new Instruction(kInstAddress, state_);
-    instruction_->set_size(4);
-  }
-
-  // Creates source and destination scalar register operands for the registers
-  // named in the two vectors and append them to the given instruction.
-  void AppendRegisterOperands(Instruction *inst,
-                              const std::vector<std::string> &sources,
-                              const std::vector<std::string> &destinations) {
-    for (auto &reg_name : sources) {
-      auto *reg = state_->GetRegister<CheriotRegister>(reg_name).first;
-      inst->AppendSource(reg->CreateSourceOperand());
-    }
-    for (auto &reg_name : destinations) {
-      auto *reg = state_->GetRegister<CheriotRegister>(reg_name).first;
-      inst->AppendDestination(reg->CreateDestinationOperand(0));
-    }
-  }
-
-  // Creates source and destination scalar register operands for the registers
-  // named in the two vectors and append them to the default instruction.
-  void AppendRegisterOperands(const std::vector<std::string> &sources,
-                              const std::vector<std::string> &destinations) {
-    AppendRegisterOperands(instruction_, sources, destinations);
-  }
-
-  // named register and sets it to the corresponding value.
-  template <typename T, typename RegisterType = CheriotRegister>
-  void SetRegisterValues(
-      const std::vector<std::tuple<std::string, const T>> &values) {
-    for (auto &[reg_name, value] : values) {
-      auto *reg = state_->GetRegister<RegisterType>(reg_name).first;
-      auto *db =
-          state_->db_factory()->Allocate<typename RegisterType::ValueType>(1);
-      db->template Set<T>(0, value);
-      reg->SetDataBuffer(db);
-      db->DecRef();
-    }
-  }
-
-  // Initializes the semantic function of the instruction object.
-  void SetSemanticFunction(Instruction *inst,
-                           Instruction::SemanticFunction fcn) {
-    inst->set_semantic_function(fcn);
-  }
-
-  // Initializes the semantic function for the default instruction.
-  void SetSemanticFunction(Instruction::SemanticFunction fcn) {
-    instruction_->set_semantic_function(fcn);
-  }
-
-  // Sets the default child instruction as the child of the default instruction.
-  void SetChildInstruction() { instruction_->AppendChild(child_instruction_); }
-
-  // Initializes the semantic function for the default child instruction.
-  void SetChildSemanticFunction(Instruction::SemanticFunction fcn) {
-    child_instruction_->set_semantic_function(fcn);
-  }
-
-  // Construct a random FP value by separately generating integer values for
-  // sign, exponent and mantissa.
-  template <typename T>
-  T RandomFPValue() {
-    using UInt = typename FPTypeInfo<T>::IntType;
-    UInt sign = absl::Uniform(absl::IntervalClosed, bitgen_, 0ULL, 1ULL);
-    UInt exp = absl::Uniform(absl::IntervalClosedOpen, bitgen_, 0ULL,
-                             1ULL << FPTypeInfo<T>::kExpSize);
-    UInt sig = absl::Uniform(absl::IntervalClosedOpen, bitgen_, 0ULL,
-                             1ULL << FPTypeInfo<T>::kSigSize);
-    UInt value = (sign & 1) << (FPTypeInfo<T>::kBitSize - 1) |
-                 (exp << FPTypeInfo<T>::kSigSize) | sig;
-    T val = *reinterpret_cast<T *>(&value);
-    return val;
-  }
-
-  // This method uses random values for each field in the fp number.
-  template <typename T>
-  void FillArrayWithRandomFPValues(absl::Span<T> span) {
-    for (auto &val : span) {
-      val = RandomFPValue<T>();
-    }
-  }
-
-  template <typename R, typename LHS>
-  void UnaryOpFPTestHelper(absl::string_view name, Instruction *inst,
-                           absl::Span<const absl::string_view> reg_prefixes,
-                           int delta_position,
-                           std::function<R(LHS)> operation) {
-    using LhsRegisterType = RVFpRegister;
-    using DestRegisterType = RVFpRegister;
-    LHS lhs_values[kTestValueLength];
-    auto lhs_span = absl::Span<LHS>(lhs_values);
-    const std::string kR1Name = absl::StrCat(reg_prefixes[0], 1);
-    const std::string kRdName = absl::StrCat(reg_prefixes[1], 5);
-    // This is used for the rounding mode operand.
-    const std::string kRmName = absl::StrCat("x", 10);
-    AppendRegisterOperands({kR1Name, kRmName}, {kRdName});
-    FillArrayWithRandomFPValues<LHS>(lhs_span);
-    using LhsInt = typename FPTypeInfo<LHS>::IntType;
-    *reinterpret_cast<LhsInt *>(&lhs_span[0]) = FPTypeInfo<LHS>::kQNaN;
-    *reinterpret_cast<LhsInt *>(&lhs_span[1]) = FPTypeInfo<LHS>::kSNaN;
-    *reinterpret_cast<LhsInt *>(&lhs_span[2]) = FPTypeInfo<LHS>::kPosInf;
-    *reinterpret_cast<LhsInt *>(&lhs_span[3]) = FPTypeInfo<LHS>::kNegInf;
-    *reinterpret_cast<LhsInt *>(&lhs_span[4]) = FPTypeInfo<LHS>::kPosZero;
-    *reinterpret_cast<LhsInt *>(&lhs_span[5]) = FPTypeInfo<LHS>::kNegZero;
-    *reinterpret_cast<LhsInt *>(&lhs_span[6]) = FPTypeInfo<LHS>::kPosDenorm;
-    *reinterpret_cast<LhsInt *>(&lhs_span[7]) = FPTypeInfo<LHS>::kNegDenorm;
-    for (int i = 0; i < kTestValueLength; i++) {
-      SetRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}});
-
-      for (int rm : {0, 1, 2, 3, 4}) {
-        rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
-        SetRegisterValues<int, CheriotRegister>({{kRmName, rm}});
-        SetRegisterValues<R, DestRegisterType>({{kRdName, 0}});
-
-        inst->Execute(nullptr);
-
-        R op_val;
-        {
-          ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface());
-          op_val = operation(lhs_span[i]);
-        }
-        auto reg_val = state_->GetRegister<DestRegisterType>(kRdName)
-                           .first->data_buffer()
-                           ->template Get<R>(0);
-        FPCompare<R>(op_val, reg_val, delta_position,
-                     absl::StrCat(name, "  ", i, ": ", lhs_span[i]));
-      }
-    }
-  }
-
-  // Tester for unary instructions that produce an exception flag value.
-  template <typename R, typename LHS>
-  void UnaryOpWithFflagsFPTestHelper(
-      absl::string_view name, Instruction *inst,
-      absl::Span<const absl::string_view> reg_prefixes, int delta_position,
-      std::function<std::tuple<R, uint32_t>(LHS)> operation) {
-    using LhsRegisterType = RVFpRegister;
-    using DestRegisterType = RVFpRegister;
-    using LhsInt = typename FPTypeInfo<LHS>::IntType;
-    using RInt = typename FPTypeInfo<R>::IntType;
-    LHS lhs_values[kTestValueLength];
-    auto lhs_span = absl::Span<LHS>(lhs_values);
-    const std::string kR1Name = absl::StrCat(reg_prefixes[0], 1);
-    const std::string kRdName = absl::StrCat(reg_prefixes[1], 5);
-    // This is used for the rounding mode operand.
-    const std::string kRmName = absl::StrCat("x", 10);
-    AppendRegisterOperands({kR1Name, kRmName}, {kRdName});
-    auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags");
-    instruction_->AppendDestination(flag_op);
-    FillArrayWithRandomFPValues<LHS>(lhs_span);
-    *reinterpret_cast<LhsInt *>(&lhs_span[0]) = FPTypeInfo<LHS>::kQNaN;
-    *reinterpret_cast<LhsInt *>(&lhs_span[1]) = FPTypeInfo<LHS>::kSNaN;
-    *reinterpret_cast<LhsInt *>(&lhs_span[2]) = FPTypeInfo<LHS>::kPosInf;
-    *reinterpret_cast<LhsInt *>(&lhs_span[3]) = FPTypeInfo<LHS>::kNegInf;
-    *reinterpret_cast<LhsInt *>(&lhs_span[4]) = FPTypeInfo<LHS>::kPosZero;
-    *reinterpret_cast<LhsInt *>(&lhs_span[5]) = FPTypeInfo<LHS>::kNegZero;
-    *reinterpret_cast<LhsInt *>(&lhs_span[6]) = FPTypeInfo<LHS>::kPosDenorm;
-    *reinterpret_cast<LhsInt *>(&lhs_span[7]) = FPTypeInfo<LHS>::kNegDenorm;
-    for (int i = 0; i < kTestValueLength; i++) {
-      SetRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}});
-
-      for (int rm : {0, 1, 2, 3, 4}) {
-        rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
-        rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
-        SetRegisterValues<int, CheriotRegister>({{kRmName, rm}, {}});
-        SetRegisterValues<R, DestRegisterType>({{kRdName, 0}});
-
-        inst->Execute(nullptr);
-        auto fflags = rv_fp_->fflags()->GetUint32();
-
-        R op_val;
-        uint32_t flag;
-        {
-          ScopedFPRoundingMode scoped_rm(rv_fp_->host_fp_interface(), rm);
-          std::tie(op_val, flag) = operation(lhs_span[i]);
-        }
-
-        auto reg_val = state_->GetRegister<DestRegisterType>(kRdName)
-                           .first->data_buffer()
-                           ->template Get<R>(0);
-        FPCompare<R>(
-            op_val, reg_val, delta_position,
-            absl::StrCat(name, "  ", i, ": ", lhs_span[i], " rm: ", rm));
-        auto lhs_uint = *reinterpret_cast<LhsInt *>(&lhs_span[i]);
-        auto op_val_uint = *reinterpret_cast<RInt *>(&op_val);
-        EXPECT_EQ(flag, fflags)
-            << name << "(" << lhs_span[i] << ")  " << std::hex << name << "(0x"
-            << lhs_uint << ") == " << op_val << std::hex << "  0x"
-            << op_val_uint << " rm: " << rm;
-      }
-    }
-  }
-
-  // Test helper for binary fp instructions.
-  template <typename R, typename LHS, typename RHS>
-  void BinaryOpFPTestHelper(absl::string_view name, Instruction *inst,
-                            absl::Span<const absl::string_view> reg_prefixes,
-                            int delta_position,
-                            std::function<R(LHS, RHS)> operation) {
-    using LhsRegisterType = RVFpRegister;
-    using RhsRegisterType = RVFpRegister;
-    using DestRegisterType = RVFpRegister;
-    LHS lhs_values[kTestValueLength];
-    RHS rhs_values[kTestValueLength];
-    auto lhs_span = absl::Span<LHS>(lhs_values);
-    auto rhs_span = absl::Span<RHS>(rhs_values);
-    const std::string kR1Name = absl::StrCat(reg_prefixes[0], 1);
-    const std::string kR2Name = absl::StrCat(reg_prefixes[1], 2);
-    const std::string kRdName = absl::StrCat(reg_prefixes[2], 5);
-    // This is used for the rounding mode operand.
-    const std::string kRmName = absl::StrCat("x", 10);
-    AppendRegisterOperands({kR1Name, kR2Name, kRmName}, {kRdName});
-    auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags");
-    instruction_->AppendDestination(flag_op);
-    FillArrayWithRandomFPValues<LHS>(lhs_span);
-    FillArrayWithRandomFPValues<RHS>(rhs_span);
-    using LhsInt = typename FPTypeInfo<LHS>::IntType;
-    *reinterpret_cast<LhsInt *>(&lhs_span[0]) = FPTypeInfo<LHS>::kQNaN;
-    *reinterpret_cast<LhsInt *>(&lhs_span[1]) = FPTypeInfo<LHS>::kSNaN;
-    *reinterpret_cast<LhsInt *>(&lhs_span[2]) = FPTypeInfo<LHS>::kPosInf;
-    *reinterpret_cast<LhsInt *>(&lhs_span[3]) = FPTypeInfo<LHS>::kNegInf;
-    *reinterpret_cast<LhsInt *>(&lhs_span[4]) = FPTypeInfo<LHS>::kPosZero;
-    *reinterpret_cast<LhsInt *>(&lhs_span[5]) = FPTypeInfo<LHS>::kNegZero;
-    *reinterpret_cast<LhsInt *>(&lhs_span[6]) = FPTypeInfo<LHS>::kPosDenorm;
-    *reinterpret_cast<LhsInt *>(&lhs_span[7]) = FPTypeInfo<LHS>::kNegDenorm;
-    for (int i = 0; i < kTestValueLength; i++) {
-      SetRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}});
-      SetRegisterValues<RHS, RhsRegisterType>({{kR2Name, rhs_span[i]}});
-
-      for (int rm : {0, 1, 2, 3, 4}) {
-        rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
-        SetRegisterValues<int, CheriotRegister>({{kRmName, rm}});
-        SetRegisterValues<R, DestRegisterType>({{kRdName, 0}});
-
-        inst->Execute(nullptr);
-
-        R op_val;
-        {
-          ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface());
-          op_val = operation(lhs_span[i], rhs_span[i]);
-        }
-        auto reg_val = state_->GetRegister<DestRegisterType>(kRdName)
-                           .first->data_buffer()
-                           ->template Get<R>(0);
-        FPCompare<R>(op_val, reg_val, delta_position,
-                     absl::StrCat(name, "  ", i, ": ", lhs_span[i], "  ",
-                                  rhs_span[i], " rm: ", rm));
-      }
-      if (HasFailure()) return;
-    }
-  }
-
-  // Test helper for binary instructions that also produce an exception flag
-  // value.
-  template <typename R, typename LHS, typename RHS>
-  void BinaryOpWithFflagsFPTestHelper(
-      absl::string_view name, Instruction *inst,
-      absl::Span<const absl::string_view> reg_prefixes, int delta_position,
-      std::function<std::tuple<R, uint32_t>(LHS, RHS)> operation) {
-    using LhsRegisterType = RVFpRegister;
-    using RhsRegisterType = RVFpRegister;
-    using DestRegisterType = RVFpRegister;
-    using LhsUInt = typename FPTypeInfo<LHS>::IntType;
-    using RhsUInt = typename FPTypeInfo<RHS>::IntType;
-    LHS lhs_values[kTestValueLength];
-    RHS rhs_values[kTestValueLength];
-    auto lhs_span = absl::Span<LHS>(lhs_values);
-    auto rhs_span = absl::Span<RHS>(rhs_values);
-    const std::string kR1Name = absl::StrCat(reg_prefixes[0], 1);
-    const std::string kR2Name = absl::StrCat(reg_prefixes[1], 2);
-    const std::string kRdName = absl::StrCat(reg_prefixes[2], 5);
-    // This is used for the rounding mode operand.
-    const std::string kRmName = absl::StrCat("x", 10);
-    AppendRegisterOperands({kR1Name, kR2Name, kRmName}, {kRdName});
-    auto *flag_op = rv_fp_->fflags()->CreateSetDestinationOperand(0, "fflags");
-    instruction_->AppendDestination(flag_op);
-    FillArrayWithRandomFPValues<LHS>(lhs_span);
-    FillArrayWithRandomFPValues<RHS>(rhs_span);
-    using LhsInt = typename FPTypeInfo<LHS>::IntType;
-    *reinterpret_cast<LhsInt *>(&lhs_span[0]) = FPTypeInfo<LHS>::kQNaN;
-    *reinterpret_cast<LhsInt *>(&lhs_span[1]) = FPTypeInfo<LHS>::kSNaN;
-    *reinterpret_cast<LhsInt *>(&lhs_span[2]) = FPTypeInfo<LHS>::kPosInf;
-    *reinterpret_cast<LhsInt *>(&lhs_span[3]) = FPTypeInfo<LHS>::kNegInf;
-    *reinterpret_cast<LhsInt *>(&lhs_span[4]) = FPTypeInfo<LHS>::kPosZero;
-    *reinterpret_cast<LhsInt *>(&lhs_span[5]) = FPTypeInfo<LHS>::kNegZero;
-    *reinterpret_cast<LhsInt *>(&lhs_span[6]) = FPTypeInfo<LHS>::kPosDenorm;
-    *reinterpret_cast<LhsInt *>(&lhs_span[7]) = FPTypeInfo<LHS>::kNegDenorm;
-    for (int i = 0; i < kTestValueLength; i++) {
-      SetRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}});
-      SetRegisterValues<RHS, RhsRegisterType>({{kR2Name, rhs_span[i]}});
-
-      for (int rm : {0, 1, 2, 3, 4}) {
-        rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
-        rv_fp_->fflags()->Write(static_cast<uint32_t>(0));
-        SetRegisterValues<int, CheriotRegister>({{kRmName, rm}, {}});
-        SetRegisterValues<R, DestRegisterType>({{kRdName, 0}});
-
-        inst->Execute(nullptr);
-
-        R op_val;
-        uint32_t flag;
-        {
-          ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface());
-          std::tie(op_val, flag) = operation(lhs_span[i], rhs_span[i]);
-        }
-        auto reg_val = state_->GetRegister<DestRegisterType>(kRdName)
-                           .first->data_buffer()
-                           ->template Get<R>(0);
-        FPCompare<R>(
-            op_val, reg_val, delta_position,
-            absl::StrCat(name, "  ", i, ": ", lhs_span[i], "  ", rhs_span[i]));
-        auto lhs_uint = *reinterpret_cast<LhsUInt *>(&lhs_span[i]);
-        auto rhs_uint = *reinterpret_cast<RhsUInt *>(&rhs_span[i]);
-        auto fflags = rv_fp_->fflags()->GetUint32();
-        EXPECT_EQ(flag, fflags)
-            << std::hex << name << "(" << lhs_uint << ", " << rhs_uint << ")";
-      }
-    }
-  }
-
-  template <typename R, typename LHS, typename MHS, typename RHS>
-  void TernaryOpFPTestHelper(absl::string_view name, Instruction *inst,
-                             absl::Span<const absl::string_view> reg_prefixes,
-                             int delta_position,
-                             std::function<R(LHS, MHS, RHS)> operation) {
-    using LhsRegisterType = RVFpRegister;
-    using MhsRegisterType = RVFpRegister;
-    using RhsRegisterType = RVFpRegister;
-    using DestRegisterType = RVFpRegister;
-    LHS lhs_values[kTestValueLength];
-    MHS mhs_values[kTestValueLength];
-    RHS rhs_values[kTestValueLength];
-    auto lhs_span = absl::Span<LHS>(lhs_values);
-    auto mhs_span = absl::Span<MHS>(mhs_values);
-    auto rhs_span = absl::Span<RHS>(rhs_values);
-    const std::string kR1Name = absl::StrCat(reg_prefixes[0], 1);
-    const std::string kR2Name = absl::StrCat(reg_prefixes[1], 2);
-    const std::string kR3Name = absl::StrCat(reg_prefixes[2], 3);
-    const std::string kRdName = absl::StrCat(reg_prefixes[3], 5);
-    // This is used for the rounding mode operand.
-    const std::string kRmName = absl::StrCat("x", 10);
-    AppendRegisterOperands({kR1Name, kR2Name, kR3Name, kRmName}, {kRdName});
-    FillArrayWithRandomFPValues<LHS>(lhs_span);
-    FillArrayWithRandomFPValues<MHS>(mhs_span);
-    FillArrayWithRandomFPValues<RHS>(rhs_span);
-    using LhsInt = typename FPTypeInfo<LHS>::IntType;
-    *reinterpret_cast<LhsInt *>(&lhs_span[0]) = FPTypeInfo<LHS>::kQNaN;
-    *reinterpret_cast<LhsInt *>(&lhs_span[1]) = FPTypeInfo<LHS>::kSNaN;
-    *reinterpret_cast<LhsInt *>(&lhs_span[2]) = FPTypeInfo<LHS>::kPosInf;
-    *reinterpret_cast<LhsInt *>(&lhs_span[3]) = FPTypeInfo<LHS>::kNegInf;
-    *reinterpret_cast<LhsInt *>(&lhs_span[4]) = FPTypeInfo<LHS>::kPosZero;
-    *reinterpret_cast<LhsInt *>(&lhs_span[5]) = FPTypeInfo<LHS>::kNegZero;
-    *reinterpret_cast<LhsInt *>(&lhs_span[6]) = FPTypeInfo<LHS>::kPosDenorm;
-    *reinterpret_cast<LhsInt *>(&lhs_span[7]) = FPTypeInfo<LHS>::kNegDenorm;
-    for (int i = 0; i < kTestValueLength; i++) {
-      SetRegisterValues<LHS, LhsRegisterType>({{kR1Name, lhs_span[i]}});
-      SetRegisterValues<MHS, MhsRegisterType>({{kR2Name, mhs_span[i]}});
-      SetRegisterValues<RHS, RhsRegisterType>({{kR3Name, rhs_span[i]}});
-
-      for (int rm : {0, 1, 2, 3, 4}) {
-        rv_fp_->SetRoundingMode(static_cast<FPRoundingMode>(rm));
-        SetRegisterValues<int, CheriotRegister>({{kRmName, rm}});
-        SetRegisterValues<R, DestRegisterType>({{kRdName, 0}});
-
-        inst->Execute(nullptr);
-
-        R op_val;
-        {
-          ScopedFPStatus set_fpstatus(rv_fp_->host_fp_interface());
-          op_val = operation(lhs_span[i], mhs_span[i], rhs_span[i]);
-        }
-        auto reg_val = state_->GetRegister<DestRegisterType>(kRdName)
-                           .first->data_buffer()
-                           ->template Get<R>(0);
-        FPCompare<R>(
-            op_val, reg_val, delta_position,
-            absl::StrCat(name, "  ", i, ": ", lhs_span[i], "  ", rhs_span[i]));
-      }
-    }
-  }
-
-  absl::Span<CheriotRegister *> creg() {
-    return absl::Span<CheriotRegister *>(creg_);
-  }
-
-  absl::Span<RVFpRegister *> freg() {
-    return absl::Span<RVFpRegister *>(freg_);
-  }
-
-  absl::Span<RV64Register *> dreg() {
-    return absl::Span<RV64Register *>(dreg_);
-  }
-  absl::BitGen &bitgen() { return bitgen_; }
-  Instruction *instruction() { return instruction_; }
-
-  template <typename T>
-  T RoundToInteger(T val) {
-    using FromUint = typename FPTypeInfo<T>::IntType;
-    auto constexpr kBias = FPTypeInfo<T>::kExpBias;
-    auto constexpr kExpMask = FPTypeInfo<T>::kExpMask;
-    auto constexpr kSigSize = FPTypeInfo<T>::kSigSize;
-    auto constexpr kSigMask = FPTypeInfo<T>::kSigMask;
-    FromUint val_u = *reinterpret_cast<FromUint *>(&val);
-    FromUint exp = kExpMask & val_u;
-    FromUint sign = val_u & (1ULL << (FPTypeInfo<T>::kBitSize - 1));
-    int exp_value = exp >> kSigSize;
-    FromUint sig = kSigMask & val_u;
-    // Turn the value into a denormal.
-    constexpr FromUint hidden = 1ULL << (kSigSize - 1);
-    FromUint tmp_u = sign | ((exp != 0) ? hidden : 0ULL) | (sig >> 1);
-    T tmp = *reinterpret_cast<T *>(&tmp_u);
-    if ((exp_value >= kBias) && (exp_value - kBias + 1 < kSigSize)) {
-      // Divide so that only the bits we care about are left in the significand.
-      int shift = kBias + kSigSize - exp_value - 1;
-      FromUint div_exp = shift + kBias;
-      FromUint div_u = div_exp << kSigSize;
-      T div = *reinterpret_cast<T *>(&div_u);
-      tmp /= div;
-      // Convert back to normalized number, by using the original sign
-      // and exponent, and the normalized and significand from the division.
-      tmp_u = *reinterpret_cast<FromUint *>(&tmp);
-      val_u = sign | exp | ((tmp_u << (shift + 1)) & kSigMask);
-      val = *reinterpret_cast<T *>(&val_u);
-    }
-    return val;
-  }
-
- protected:
-  CheriotRegister *creg_[32];
-  RV64Register *dreg_[32];
-  RVFpRegister *freg_[32];
-  CheriotState *state_;
-  Instruction *instruction_;
-  Instruction *child_instruction_;
-  TaggedFlatDemandMemory *memory_;
-  RiscVCheriotFPState *rv_fp_;
-  absl::BitGen bitgen_;
-};
-
-}  // namespace test
-}  // namespace cheriot
-}  // namespace sim
-}  // namespace mpact
-
-#endif  // MPACT_CHERIOT___TEST_RISCV_CHERIOT_FP_TEST_BASE_H_
diff --git a/cheriot/test/riscv_cheriot_i_instructions_test.cc b/cheriot/test/riscv_cheriot_i_instructions_test.cc
index 93dbd41..d5ab010 100644
--- a/cheriot/test/riscv_cheriot_i_instructions_test.cc
+++ b/cheriot/test/riscv_cheriot_i_instructions_test.cc
@@ -28,6 +28,7 @@
 #include "mpact/sim/generic/immediate_operand.h"
 #include "mpact/sim/generic/instruction.h"
 #include "mpact/sim/generic/type_helpers.h"
+#include "mpact/sim/util/memory/tagged_flat_demand_memory.h"
 
 // This file contains tests for individual RiscV32I instructions.
 
@@ -38,6 +39,7 @@
 using ::mpact::sim::cheriot::CheriotState;
 using ::mpact::sim::generic::ImmediateOperand;
 using ::mpact::sim::generic::Instruction;
+using ::mpact::sim::util::TaggedFlatDemandMemory;
 using CH_EC = ::mpact::sim::cheriot::ExceptionCode;
 using PB = CheriotRegister::PermissionBits;
 
@@ -62,7 +64,8 @@
 class RVCheriotIInstructionTest : public testing::Test {
  public:
   RVCheriotIInstructionTest() {
-    state_ = new CheriotState("test");
+    mem_ = new TaggedFlatDemandMemory(8);
+    state_ = new CheriotState("test", mem_, nullptr);
     instruction_ = new Instruction(kInstAddress, state_);
     instruction_->set_size(4);
     creg_1_ = state_->GetRegister<CheriotRegister>(kC1).first;
@@ -76,6 +79,7 @@
   }
 
   ~RVCheriotIInstructionTest() override {
+    delete mem_;
     delete state_;
     delete instruction_;
   }
@@ -135,6 +139,7 @@
                    uint64_t exception_code, uint64_t epc,
                    const Instruction *inst);
 
+  TaggedFlatDemandMemory *mem_;
   CheriotState *state_;
   Instruction *instruction_;
   CheriotRegister *creg_1_;
diff --git a/cheriot/test/riscv_cheriot_m_instructions_test.cc b/cheriot/test/riscv_cheriot_m_instructions_test.cc
index 4874485..ec23951 100644
--- a/cheriot/test/riscv_cheriot_m_instructions_test.cc
+++ b/cheriot/test/riscv_cheriot_m_instructions_test.cc
@@ -28,6 +28,7 @@
 #include "mpact/sim/generic/immediate_operand.h"
 #include "mpact/sim/generic/instruction.h"
 #include "mpact/sim/generic/type_helpers.h"
+#include "mpact/sim/util/memory/tagged_flat_demand_memory.h"
 
 // This file contains tests for individual CHERIoT RiscV32M instructions.
 
@@ -38,6 +39,7 @@
 using ::mpact::sim::cheriot::CheriotState;
 using ::mpact::sim::generic::ImmediateOperand;
 using ::mpact::sim::generic::Instruction;
+using ::mpact::sim::util::TaggedFlatDemandMemory;
 using CH_EC = ::mpact::sim::cheriot::ExceptionCode;
 using PB = CheriotRegister::PermissionBits;
 
@@ -64,7 +66,8 @@
 class RVCheriotMInstructionTest : public testing::Test {
  public:
   RVCheriotMInstructionTest() {
-    state_ = new CheriotState("test");
+    mem_ = new TaggedFlatDemandMemory(8);
+    state_ = new CheriotState("test", mem_, nullptr);
     instruction_ = new Instruction(kInstAddress, state_);
     instruction_->set_size(4);
     creg_1_ = state_->GetRegister<CheriotRegister>(kC1).first;
@@ -73,6 +76,7 @@
   }
 
   ~RVCheriotMInstructionTest() override {
+    delete mem_;
     delete state_;
     delete instruction_;
   }
@@ -128,6 +132,7 @@
     return reg->address();
   }
 
+  TaggedFlatDemandMemory *mem_;
   CheriotState *state_;
   Instruction *instruction_;
   CheriotRegister *creg_1_;
diff --git a/cheriot/test/riscv_cheriot_zicsr_instructions_test.cc b/cheriot/test/riscv_cheriot_zicsr_instructions_test.cc
index a1792af..6d75d87 100644
--- a/cheriot/test/riscv_cheriot_zicsr_instructions_test.cc
+++ b/cheriot/test/riscv_cheriot_zicsr_instructions_test.cc
@@ -28,6 +28,7 @@
 #include "googlemock/include/gmock/gmock.h"
 #include "mpact/sim/generic/immediate_operand.h"
 #include "mpact/sim/generic/instruction.h"
+#include "mpact/sim/util/memory/tagged_flat_demand_memory.h"
 #include "riscv//riscv_csr.h"
 
 // This file contains tests for individual Zicsr instructions.
@@ -42,6 +43,7 @@
 using ::mpact::sim::generic::ImmediateOperand;
 using ::mpact::sim::generic::Instruction;
 using ::mpact::sim::riscv::RiscV32SimpleCsr;
+using ::mpact::sim::util::TaggedFlatDemandMemory;
 
 constexpr uint32_t kInstAddress = 0x2468;
 
@@ -59,7 +61,8 @@
 class ZicsrInstructionsTest : public testing::Test {
  protected:
   ZicsrInstructionsTest() {
-    state_ = new CheriotState("test");
+    mem_ = new TaggedFlatDemandMemory(8);
+    state_ = new CheriotState("test", mem_, nullptr);
     instruction_ = new Instruction(kInstAddress, state_);
     instruction_->set_size(4);
     state_->set_on_trap([this](bool is_interrupt, uint64_t trap_value,
@@ -71,6 +74,7 @@
 
   ~ZicsrInstructionsTest() override {
     delete instruction_;
+    delete mem_;
     delete state_;
   }
 
@@ -130,6 +134,7 @@
                    uint64_t exception_code, uint64_t epc,
                    const Instruction *inst);
 
+  TaggedFlatDemandMemory *mem_;
   RiscV32SimpleCsr *csr_;
   CheriotState *state_;
   Instruction *instruction_;